diff --git a/P2996.md b/P2996.md new file mode 100644 index 0000000000000..d77cabdadaf51 --- /dev/null +++ b/P2996.md @@ -0,0 +1,277 @@ +# Clang/P2996 + +Clang/P2996 is a fork of the [llvm/llvm-project](https://github.com/llvm/llvm-project) implementing experimental support for ISO C++ (WG21) proposal [**P2996**](https://wg21.link/p2996) (_Reflection for C++26_) in the `clang` compiler front-end. This project was initiated by a passionate group of C++ enthusiasts in [Bloomberg's Engineering department](https://www.TechAtBloomberg.com). + +The Clang/P2996 fork is highly experimental; sharp edges abound and occasional crashes should be expected. Memory usage has not been optimized and is, in many cases, wasteful. **DO NOT use this project to build any artifacts destined for production.** + +That said, this project represents one of the most complete known implementations of P2996 to date. It is possible to use Clang/P2996 to build nontrivial reflection-heavy programs. We encourage all interested parties to try it out, and we welcome any pull requests or feedback. + +The intent of this project is to continue tracking changes to P2996 as it makes its way through WG21's stages of development. That said, this is an aspiration and not a promise. The project is provided "as is." Subsequent development will be on a "best effort" basis, and we offer no guarantees as to its continued maintenance. + + +## Menu + +- [Rationale](#rationale) +- [Acknowledgments](#acknowledgments) +- [Quick start](#quick-start) +- [Implementation status](#implementation-status) + - [Incomplete features](#incomplete-features) + - [P2996 test cases](#p2996-test-cases) +- [License](#license) +- [Code of conduct](#code-of-conduct) +- [Implementation design notes](#implementation-design-notes) + - [Representing reflections](#representing-reflections) + - [Reflect expressions](#reflect-expressions) + - [Splices](#splices) + - [Metafunctions](#metafunctions) + - [Tracking source locations](#tracking-source-locations) + - [Name mangling](#name-mangling) +- [Update history](#update-history) + + +## Rationale +The project's primary goal is to explore the implementation feasibility of P2996 in a major open source compiler. Through this, we hope to also provide an environment in which C++ enthusiasts can experiment with additional reflection features not already proposed by P2996. Finally, we aim to discover and raise awareness of possible concerns with bringing P2996 to `clang` (i.e., proposed features that may not fit easily within the existing architecture). + + +## Acknowledgments +This project owes an enormous debt to the historical [paper/p2320](https://github.com/lock3/meta/tree/paper/p2320) LLVM fork previously developed by Andrew Sutton and Wyatt Childers from their time at Lock3 Software. Their work not only provided our initial roadmap for implementing reflection in `clang`, but also taught us much about `clang`'s internals -- none of the initial Clang/P2996 developers had any prior development experience with `clang` (or indeed, any modern C++ compiler). + +The initial implementation of Clang/P2996 was led by Dan Katz, with significant contributions from Georgi Koyrushki, Mark Sciabica, Sergei Murzin, and Kay Hicketts. All are engineers at Bloomberg, and they are grateful to their organization for supporting this work. + + +## Quick start +The upstream LLVM project provides an excellent [Getting Started](https://llvm.org/docs/GettingStarted.html) guide with instructions for building `clang` and `libc++`. After building both of these targets, support for reflection can be enabled in Clang/P2996 by compiling with both the `-std=c++26` and `-freflection` flags. + +The following additional experimental features can be enabled on top of `-freflection`: +- Metafunction extensions proposed for [P3096](https://wg21.link/p3096) (`-fparameter-reflection`). + + +## Implementation status +At present, Clang/P2996 supports: + +* Reflection over all entities supported by P2996 (i.e., types, functions, variables, class members, templates, namespaces, constant values) +* Splicing types, expressions, and namespaces +* All metafunctions propopsed by P2996 (except where noted in the [issue tracker](https://github.com/bloomberg/clang-p2996/issues)) +* P3096 metafunction extensions for function parameters (enabled with `-fparameter-reflection`) + +Any implemented language or library extensions that are _not_ candidates for inclusion in P2996 will be enabled by separate feature flags. Proposed features like expansion statements (i.e., `template for`) and non-transient `constexpr` allocation are not supported at this time. Any behaviors divergent from what is described by P2996, unless implemented experimentally for possible adoption by P2996, should be considered bugs. + +One sharp edge worth mentioning is that our current implementation of P2996 metafunctions is regrettably resistant to proper AST serialization. As such, this compiler cannot, in its current state, be safely used to build precompiled headers or C++20 modules that contain reflection features. This is a difficult problem that is discussed in greater detail below. We are very interested in identifying a design that can elegantly address this issue. + +### Incomplete features +A few features and behavior from P2996 are not yet supported (e.g., template splicers). The [issue tracker](https://github.com/bloomberg/clang-p2996/issues) will be kept updated with all known bugs, noncomformant behaviors, and missing features. + +Our goal has been to provide a working compiler capable of building executables. It is currently a non-goal to support the full family of features and tools offered by `clang` (e.g., AST output, `clang-tidy`, etc). We have therefore taken several shortcuts when it comes to defining things like the formatting of a reflection in a text dump. We are open to pull requests implementing such things, but are otherwise content to wait until P2996 is further along in the WG21 process. + +### P2996 test cases +A significant number of tests have been written for this project, covering both the reflection and splicing operators proposed for the core language and the various metafunctions proposed for the Standard Library (found [here](/clang/test/Reflection/) and [here](/libcxx/test/std/experimental/reflection/) respectively). Among these tests are many of the examples from the P2996 paper (e.g., "_Parsing Command-Line Options II_", "_Compile-Time Ticket Counter_", and "_Emulating Typeful Reflection_"). We expect for this body of tests to continue to grow, and hope that it may furthermore assist validation of future implementations of P2996. + +At this time, our test cases only verify correct uses of P2996 facilities; that is, we have not yet written tests to validate expected diagnostics for ill-formed programs. As such, this is probably an area having a nontrivial number of bugs and crashes waiting to be discovered. + +## License +Following the example of the upstream LLVM project, new code developed for Clang/P2996 is licensed under Apache 2.0 with LLVM extensions ([LICENSE.TXT](/LICENSE.TXT)). + + +## Code of conduct +The Clang/P2996 project has adopted a [Code of Conduct](https://github.com/bloomberg/.github/blob/main/CODE_OF_CONDUCT.md). If you have any concerns about the Code, or behavior which you have experienced in the project, please contact us at opensource@bloomberg.net. + + +# Implementation design notes +Below are some high level notes about our implementation of P2996, along with some thoughts regarding certain "sharp edges" of the proposal as it relates to `clang`. + +## Representing reflections +A reflection is represented as a `ReflectionValue`: effectively a `void *` tagged with an enum value identifying the kind of entity reflected (i.e., very similar to `TemplateArgument`, `APValue`, and several other AST vocabulary types found in clang). A `ReflectionValue` is one of: + +* A type (represented by a `QualType`) +* A constant value (represented by an `ConstantExpr *`) +* A declaration of an evaluatable entity (represented by a `ValueDecl *`) +* A template (represented by a `TemplateName`) +* A namespace (represented by a `Decl *`) +* A base class specifier (represented by a `CXXBaseSpecifier *`) +* A description of a hypothetical data member, for representing the result of a call to `std::meta::data_member_spec` (represented by a `TagDataMemberSpec *`). + +This set of possible "kinds" is intended to be extensible. + +Since a constant expression can evaluate to a `ReflectionValue` scalar prvalue, `ReflectionValue` becomes a new possible kind of `APValue`. + +Since a reflection can only be spliced if it is a constant expression, the passing of reflections as template arguments is an important use case. However, the semantics of equality for reflection values do differ from those of, for example, integers. In part to implement this bespoke definition of equality, `ReflectionValue` also becomes a distinct kind of `TemplateArgument`. + + +### Retrospective +This foundational design has worked well, but a few shortcomings are worth noting. + +The use of a `ConstantExpr *` to represent a constant value was chosen as a convenient container for an `APValue`, but a smarter implementation might either use `APValue` directly. This does leave a somewhat odd cycle in which an `APValue` might hold a `ReflectionValue`, which itself might hold an `APValue`, but the use of a `ConstantExpr *`-node as a layer of indirection does nothing to make this any less weird. In fact, it necessitated several otherwise unnecessary changes to allow `ConstantExpr` to hold an `APValue` without an associated `SubExpr`. + +It's also a bit unfortunate that the "most specific" type that could be used to represent a namespace is a `Decl *`. This was chosen because a reflection of a namespace could be a `NamespaceDecl`, a `NamespaceAlias`, or a `TranslationUnitDecl` (representing the global namespace). Regrettably, the nearest common ancestor of these is `Decl` itself. + + +## Reflect expressions +We generally found parsing reflect expressions (e.g., `^int`) to be an easier problem than parsing splices. An expression of the form `^E`, where `E` is a named entity or a constant expression, is parsed as a `CXXReflectExpr` with the operand represented by a `ReflectionValue` data member. + +Several different interpretations of `E` are tentatively parsed: +1. As a template +2. As a namespace +3. As a type +4. As an expression + +The first interpretation of these to succeed is selected; if none succeed, the reflect-expression is ill-formed. + + +### Reflection contexts +The operand of a reflect expression is unevaluated, but we found that parsing reflections follows slightly different rules from other unevaluated contexts. Examples here include allowing direct reference to class members without taking their address (e.g., `^S::fn`) and resolving a namespace to a `NamespaceAliasDecl` without dealiasing. + +To implement these special rules, we introduced a new `ExpressionEvaluationContext::ReflectionContext` enum value, together with a `Sema::isReflectionContext()` method for checking whether parsing is taking place within the context of a reflect expression. + + +## Splices +While the act of reflecting over an entity always produces an expression, the act of splicing a reflection might produce: +* A type (i.e., `QualType`) +* A reference to an entity or constant expression (e.g., `ConstantExpr *`) +* A namespace + +It thus becomes clear that a _splice_, without further context, can only be given a coherent definition at the grammatical level; its semantics are dependent on both the context and the evaluation of the spliced expression. + +If the operand of the splice can be evaluated at parse time, then this presents little difficulty. However, if the splice is value-dependent on a template parameter (e.g., the common use case of splicing a reflection received as a template argument), then it may not be possible to determine what the splice "is" until template substitution. P2996 addresses this in a familiar way, requiring that all such splices be preceded with e.g., a leading `typename` to disambiguate the class of entity; an expression is assumed in the absence of such a disambiguator. + +To help with such cases, we decompose a splice `[:R:]` into two objects: +1. A `CXXIndeterminateSpliceExpr` representing the expression whose evaluation will yield the `ReflectionValue` of the entity being spliced, and +2. An AST object holding a pointer to the `CXXIndeterminateSpliceExpr` (e.g., `ReflectionSpliceType` for types, `CXXExprSpliceExpr` for expressions). + + +### Parsing splices of expressions +Splices of expressions usually appear in the context of a _cast-expression_ (e.g., `[:R:] + 13`), but they can also appear on the right-hand side of a _member access expression_. The existing machinery in `SemaMemberExpr.cpp` assumes that the member being accessed has not yet been resolved and performs name lookup to find a `Decl`. However, in the case of an expression like `a.[:R:]`, we already have the `Decl` from a `ReflectionValue` obtained through evaluation of `R`. Therefore, we introduce overloads of functions like `Sema::ActOnMemberAccessExpr` to support the case where the right-hand of the member access is a splice. A special `CXXDependentMemberSpliceExpr` is introduced to cover cases when the reflection is dependent on a template parameter, in order to "hold" the underlying `CXXIndeterminateSpliceExpr` until template substitution. + + +### Nested-name-specifiers containing splices +Splices of types and namespaces can appear as the (possibly dependent) leading component of a nested-name-specifier. This is interesting in cases such as `typename [:R:]::member`, for which the `typename` keyword clarifies that `member` will resolve to a type, but tells us nothing about what sort of entity `[:R:]` might be. We address this by adding a new `SpecifierKind::IndeterminateSplice` kind to `NestedNameSpecifier`, which holds a `CXXIndeterminateSpliceExpr` to be rebuilt thereafter into a namespace or type during template substitution. + + +### Splicing namespaces +The most recent revisions of P2996 remove the `namespace [: :]` syntax and only allow splices of namespaces in the following contexts: +* Using directives (e.g., `using namespace [:R:];`) +* Namespace alias definitions (e.g., `namespace A = [:R:];`) +* Within nested-name-specifiers (e.g., `[:R:]::Member`) + +Use of splices in namespace definitions (e.g., `namespace [:R:] { ... }`) is neither supported by this compiler nor proposed by P2996. + +Since both using directives and namespace aliases may appear in templated contexts, it becomes possible to have a namespace name that is dependent on a template parameter. Support for this has not yet been implemented in this compiler. + + +## Metafunctions +The library functions proposed by P2996 for producing and operating on reflections are coloquially referred to as _metafunctions_; as a general rule, these functions cannot be implemented without compiler intrinsics. Drawing inspiration from the earlier `paper/p2320` implementation from Lock3, we used a single `__metafunction` intrinsic to implement a library of 60+ metafunctions. + +An expression of the form + + __metafunction ( ID, expr0, ..., exprN ) +is parsed as a `CXXMetafunctionExpr`. The `ID` argument is expected to be a non-dependent enum constant identifying the metafunction, and is evaluated at parse time. The remaining arguments are parsed as expressions and held by the `CXXMetafunctionExpr` until constant evaluation time. The only validation performed at parse time is to check that the number of arguments is valid for the metafunction identified by `ID`. + +Corresponding to each metafunction is a global instance of the `Metafunction` class, which provides: +* The `QualType` of the value resulting from its evaluation; +* How many arguments it accepts; and +* A `Metafunction::evaluate` member function used to evaluate the metafunction for some given inputs. + +The `Metafunction::Lookup` static member function accepts a metafunction `ID` and returns a pointer to the corresponding `Metafunction` instance. + +The core of the `VisitCXXMetafunctionExpr` function, implemented in `ExprConstant.cpp` to evaluate a metafunction, invokes a callback held by the `CXXMetafunctionExpr`. In turn, this calls the appropriate `Metafunction::evaluate` function with the argument expressions held by the `CXXMetafunctionExpr`. This approach works well for our experimental purposes, but interacts poorly with precompiled headers and C++20 modules due to the unserializable nature of the evaluation callback, as detailed below. + + +### The `Sema` problem +Many of the metafunctions proposed by P2996 are "observational", in the sense that they query properties from the AST while leaving it unchanged. These include `is_type`, `is_public`, `parent_of`, and many others. + +However, several of the most powerful proposed metafunctions are _generative_: their evaluation can **modify** the state of the AST, producing side effects during constant evaluation that are not otherwise posssible in C++ today. It is exactly these side-effects that make some of the most interesting examples from the P2996 paper possible in the first place (e.g., "[Compile-time ticket counter](https://wg21.link/p2996r2#compile-time-ticket-counter)"). + +Although AST nodes can be directly constructed using only `ASTContext`, doing so elides the semantic analysis of whether the would-be entities "make any sense". This is a recipe for compiler crashes and assertion failures, since the resulting function calls, class definitions, and template instantiations will be constructed regardless of violations of invariants that later stages of compilation (e.g., CodeGen) expect semantic analysis to have already enforced. We concluded that metafunctions like `define_class`, `reflect_invoke`, and `substitute` cannot be coherently implemented in `clang` without access to the `Sema` object. + +This presents a problem for `clang` though, as its physical design is carefully architected to specifically disallow this. All constant evaluation is implemented by `AST/ExprConstant.cpp`, and there is no means of accessing the `Sema` object from this file. Indeed, referencing any part of the `Sema` layer from the `AST` layer seems contrary to the deliberate design of the `clang` codebase. + +The result is an apparent tension between the architecture of `clang` and P2996, or any other paper allowing the constant evaluation of a function to produce side effects in the AST. Between Lock3's implementation and Clang/P2996, two different approaches have been tried to address this tension. + +#### Lock3/P2320: The `EvalContext` Model +The Lock3 implementation defines a `ReflectionCallback` struct presenting a polymorphic interface to a narrow set of features provided by `Sema` (e.g., "evaluate this type trait"). This interface is defined in `/AST/`, but implementated as `ReflectionCallbackImpl` in `/Sema/`. An `EvalContext` struct, essentially an `` pair, provides the "context" needed to evaluate any compile-time expression. + +`EvalContext` replaces `ASTContext` in the interface of the `Expr::Evaluate*` family of functions. Evaluations like: +```cpp +Expr::EvalValue Result; +E->EvaluateAsRValue(Result, S.Context, /*InConstantContext=*/true); +``` + +are then replaced with calls like: +```cpp +Expr::EvalValue Result; +Expr::EvalContext Ctx(S.Context, S.GetReflectionCallbackObj()); +E->EvaluateAsRValue(Result, Ctx, /*InConstantContext=*/true); +``` + +This is a fairly invasive change, requiring modifications to many different files (especially `ExprConstant.cpp`). It also has the drawback of allowing evaluation to proceed even if the `ReflectionCallbackImpl` object isn't readily available from `Sema`, i.e., +```cpp +Expr::EvalContext Ctx(S.Context, /*ReflectionCallback=*/nullptr); +``` + +In some cases, this can result in compiler crashes, especially if a metafunction is evaluated in a context that had no means of accessing the `Sema` object. These can almost certainly be fixed, but it will require more discipline with respect to which code is allowed to evaluate expressions. + +#### Clang/P2996: The "Evaluation Callback" Model +Our implementation's approach is to bind a reference to the `Sema` object to the "evaluation" callback held by the `CXXMetafunctionExpr`. This callback takes only objects from the `AST` layer as arguments, but dispatches to the `Metafunction::evaluate` function from the `Sema` layer. The invocation from `ExprConstant.cpp` also passes an "Evaluator" callback, which allows the `Sema` metafunction implementation to evaluate an expression in the existing constant evaluation context without access to the `EvalInfo` object. This is important, for instance, to ensure that an lvalue referencing an object dynamically allocated earlier in the constant expression can be evaluated by the metafunction. + +While this requires less invasive changes than the `EvalContext` model, a new problem is introduced: the "evaluation callback" owned by the `CXXMetafunctionExpr` cannot be serialized or deserialized between processes. Since `clang` stores the serialized AST as a part of its representation of C++20 modules and precompiled headers, our proposed implementation of `CXXMetafunctionExpr` breaks both of these features. Even if we were to try to serialize the callback state, the `Sema` object referred to by the callback likely no longer exists at deserialization time: after all, the process compiling a module is likely different from the process importing it. + +Given that the `ASTReader` and `Sema` objects are owned by the same `CompilerInstance`, one may be able to construct the `ASTReader` with a reference to `Sema`, thereby allowing it to reconstruct metafunction evaluation callbacks that have references to the "new" `Sema` instance belonging to the deserializing process. We have not attempted this change, and aren't fully convinced that such a direction would be desirable. + +#### Retrospective: The `Sema` Problem +We consider this problem not fully solved. On the one hand, the `EvalContext` model avoids the storage of a non-serializable callback in the AST, obviating the issues with C++20 modules. On the other hand, it restricts the contexts from which an expression can be safely evaluated (i.e., those with access to either `Sema` or to an `EvalContext` constructed with `Sema`). The Clang/P2996 model makes it "easier" to evaluate an expression by "hiding" the `Sema` reference in the evaluation callback of the `CXXMetafunctionExpr`, but this makes (de)serialization more tricky. + +The takeaway seems to be: in the presence of the changes proposed by P2996, constant folding can no longer be performed without `Sema`. Either of the above approaches can probably be made to work in a pinch (assuming P2996's eventual adoption by WG21), but some third design approach that more fully re-imagines the relationship between `Sema` and the AST might be better yet. + + +### Retrospective: Metafunction Design +Almost all of `clang`'s machinery for evaluating constant expressions lives in `ExprConstant.cpp`, so our decision to implement P2996 metafunctions in a separate `/Sema/Metafunctions.cpp` deviates from existing practice. This model nevertheless has a few nice fatures. Implementing all such 60+ functions in an isolated file has helped delineate our "experimental" code from existing production code. More importantly, the model succeeds in maintaining the separation of `Sema` code from `AST`. + +On the other hand, implementing the metafunctions outside of `ExprConstant.cpp` means that they lack access to much of the constant evaluation state (e.g., the callstack). For metafunctions in need of access to this state, we use the evaluation of synthesized expressions as a "communication channel" with the constant evaluator. In some cases (e.g., `value_of`, `is_accessible`), this required the invention of a novel `Expr` type just to pipe the required state from the constant evaluator back to the metafunction implementation (see `LValueValueOfExpr` and `StackLocationExpr`). While this does work, it leads to wasteful allocation of AST nodes by the compiler. This increased memory usage might become noticeable when compiling large translation units, or several translation units in parallel. + + +### The difficulty of `define_class` +The family of functions used to define a tag type (e.g., `ActOnTag`, `ActOnStartDefinition`, `ActOnStartMemberDeclarations`) provide a sort of "declarative language" that is used by the `Parser` as it encounters class member definitions. Our implementation of `define_class` carefully reuses this machinery, while constructing `Scope`s and `DeclContext`s to ensure the resulting definition is a redeclaration of the provided incomplete type (and not just a new and unrelated type). Special cases are needed for when the type being completed is a specialization of a class template. The implementation ends up feeling like a sloppy and fragile reimplementation of the `Parser`, rather than a clean set of calls to existing machinery in `Sema`. + + +## Tracking source locations +Clang's ability to report accurate error messages requires it to track the locations of various tokens (e.g., expression locations, `(` and `)` for function calls, `<` and `>` for template specializations). When generating AST nodes from metafunctions or when splicing reflections, answering the question of where certain tokens are becomes awkward since we're synthesizing nodes from token sequences that are "shaped differently" than otherwise expected. While not usually a blocker in practice, the disharmony between the desire of `clang` to individually track the location of many individual tokens, and the desire of metaprogramming frameworks to synthesize a variety of AST nodes that don't easily map to the same tokens expected by `clang`, is worth noting. + + +## Name mangling +As P2996 proposes mechanisms for _Static_ Reflection, all of the business around reflections and splices and metafunctions has mostly wrapped up by the time CodeGen begins. The most notable exception is the need to mangle the names of templates having a `std::meta::info` value as an argument. As noted previously, this is a rather important use-case: since a reflection can only be spliced if it's a constant expression, a function that wants to splice a reflection that was received as an argument must receive it as a template argument. Some of the more powerful metafunctions (e.g., `substitute`, `reflect_invoke`, `value_of`) reduce the frequency with which reflections must be passed as template arguments, but it remains necessary in some cases. + +Given a specialization of `template fn()`, the compiler must be able to mangle a representation of any entity that can be reflected by `R`. This includes the full class of values of literal types - an expansion from the values of _structural_ types that can already appear as non-type template arguments. This raises questions around whether all (or which) such values should be well-formed template arguments - or perhaps, if all such values should be allowed, but some should cause the associated specialization to have internal linkage. These are questions that we expect to see clarified as P2996 advances through WG21. + +## Update history +This log will be updated sporadically to indicate which new features and bug fixes that have been added to Clang/P2996. + +### 2024-03-28 + +#### Features +- Added support for aliases to dependent namespaces (i.e., those whose definition in some way depends on a splice of a reflection dependent on a template parameter). + +#### Bug fixes +- Fixed some crashes that could occur while lexing malformed splices at global scope. +- Modified behavior of `bit_offset_of` to return offset from last byte boundary. + +### 2024-03-22 + +#### Features +- Allow splices in _using-enum-declarators_ (i.e., `using enum` statements). +- Added `can_substitute` metafunction. +- Splices of types can now appear in the _base-specifier-list_ of a class definition. +- Added metafunctions proposed for [P3096](https://wg21.link/p3096) (i.e., `parameters_of`, `has_unique_name`, and `has_default_argument`). These are only enabled when an additional `-fparameter-reflection` flag is provided. Parameter reflections may be spliced within the body of their associated function; splicing them anywhere else is ill-formed. + - `parameters_of`: Given a reflection of a function, returns a vector of reflections for the parameters. Given a reflection of a function type, returns a vector for the types of the parameters. + - `has_unique_name`: Given a reflection of a function parameter, returns whether all declarations of the function give the same name for the parameter. If this is `false`, it is unspecified which of the declared names is yielded by `name_of(^fn_param)`. + - `has_default_argument`: Given a reflection of a function parameter, returns whether the parameter has a default argument. + + +#### Bug fixes +- Corrected false diagnoses of meta types that leak into runtime code. +- Fixed crash from calling `members_of` (and related metafunctions) with reflections of primitive types. +- Support use of `define_class` with local classes declared at function scope. +- Ensure that `members_of` returns reflections of all implicit member functions. +- The `is_accessible` metafunction now works with reflections of base classes. +- Fixed crash when calling `define_class` with an instantiation of a class template parameterized by a parameter pack. + +### 2024-03-14 +- Initial release. \ No newline at end of file diff --git a/README.md b/README.md index a9b29ecbc1a3a..a6b8d52ca8572 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,10 @@ +# Clang/P2996: An LLVM fork + +Welcome to Clang/P2996, a fork of the [`llvm/llvm-project`]( +https://github.com/llvm/llvm-project) implementing experimental support for ISO +C++ proposal [P2996](https://wg21.link/p2996) in `clang`. Please see [P2996.md]( +P2996.md) for more about this project. + # The LLVM Compiler Infrastructure [![OpenSSF Scorecard](https://api.securityscorecards.dev/projects/github.com/llvm/llvm-project/badge)](https://securityscorecards.dev/viewer/?uri=github.com/llvm/llvm-project) diff --git a/clang/include/clang-c/Index.h b/clang/include/clang-c/Index.h index 7a8bd985a91fc..dace3b9bb082a 100644 --- a/clang/include/clang-c/Index.h +++ b/clang/include/clang-c/Index.h @@ -3109,6 +3109,7 @@ enum CXTemplateArgumentKind { CXTemplateArgumentKind_Declaration, CXTemplateArgumentKind_NullPtr, CXTemplateArgumentKind_Integral, + CXTemplateArgumentKind_Reflection, CXTemplateArgumentKind_Template, CXTemplateArgumentKind_TemplateExpansion, CXTemplateArgumentKind_Expression, diff --git a/clang/include/clang/AST/APValue.h b/clang/include/clang/AST/APValue.h index c4206b73b1156..b3cd04998d368 100644 --- a/clang/include/clang/AST/APValue.h +++ b/clang/include/clang/AST/APValue.h @@ -1,5 +1,7 @@ //===--- APValue.h - Union class for APFloat/APSInt/Complex -----*- C++ -*-===// // +// Copyright 2024 Bloomberg Finance L.P. +// // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception @@ -13,6 +15,7 @@ #ifndef LLVM_CLANG_AST_APVALUE_H #define LLVM_CLANG_AST_APVALUE_H +#include "clang/AST/Reflection.h" #include "clang/Basic/LLVM.h" #include "llvm/ADT/APFixedPoint.h" #include "llvm/ADT/APFloat.h" @@ -35,6 +38,7 @@ template class BasicReaderBase; class DiagnosticBuilder; class Expr; class FieldDecl; + class NamespaceDecl; struct PrintingPolicy; class Type; class ValueDecl; @@ -118,7 +122,7 @@ template<> struct PointerLikeTypeTraits { namespace clang { /// APValue - This class implements a discriminated union of [uninitialized] /// [APSInt] [APFloat], [Complex APSInt] [Complex APFloat], [Expr + Offset], -/// [Vector: N * APValue], [Array: N * APValue] +/// [Vector: N * APValue], [Array: N * APValue], [ReflectionValue] class APValue { typedef llvm::APFixedPoint APFixedPoint; typedef llvm::APSInt APSInt; @@ -140,7 +144,8 @@ class APValue { Struct, Union, MemberPointer, - AddrLabelDiff + AddrLabelDiff, + Reflection }; class LValueBase { @@ -308,7 +313,8 @@ class APValue { // We ensure elsewhere that Data is big enough for LV and MemberPointerData. typedef llvm::AlignedCharArrayUnion DataType; + UnionData, AddrLabelDiffData, + ReflectionValue> DataType; static const size_t DataSize = sizeof(DataType); DataType Data; @@ -363,6 +369,10 @@ class APValue { : Kind(None) { MakeAddrLabelDiff(); setAddrLabelDiff(LHSExpr, RHSExpr); } + APValue(ReflectionValue::ReflectionKind ReflKind, const void *ReflEntity) + : Kind(None) { + MakeReflection(ReflKind, ReflEntity); + } static APValue IndeterminateValue() { APValue Result; Result.Kind = Indeterminate; @@ -410,6 +420,7 @@ class APValue { bool isUnion() const { return Kind == Union; } bool isMemberPointer() const { return Kind == MemberPointer; } bool isAddrLabelDiff() const { return Kind == AddrLabelDiff; } + bool isReflection() const { return Kind == Reflection; } void dump() const; void dump(raw_ostream &OS, const ASTContext &Context) const; @@ -585,6 +596,21 @@ class APValue { return ((const AddrLabelDiffData *)(const char *)&Data)->RHSExpr; } + ReflectionValue &getReflection() { + assert(isReflection() && "Invalid accessor"); + return *(ReflectionValue *)(char *)&Data; + } + const ReflectionValue &getReflection() const { + return const_cast(this)->getReflection(); + } + QualType getReflectedType() const; + ConstantExpr *getReflectedConstValueExpr() const; + ValueDecl *getReflectedDecl() const; + const TemplateName getReflectedTemplate() const; + Decl *getReflectedNamespace() const; + CXXBaseSpecifier *getReflectedBaseSpecifier() const; + TagDataMemberSpec *getReflectedDataMemberSpec() const; + void setInt(APSInt I) { assert(isInt() && "Invalid accessor"); *(APSInt *)(char *)&Data = std::move(I); @@ -679,6 +705,14 @@ class APValue { new ((void *)(char *)&Data) AddrLabelDiffData(); Kind = AddrLabelDiff; } + void MakeReflection(ReflectionValue::ReflectionKind ReflKind, + const void *ReflEntity) { + assert(isAbsent() && "Bad state change"); + + new ((void *)(char *)Data.buffer) ReflectionValue( + ReflKind, const_cast(ReflEntity)); + Kind = Reflection; + } private: /// The following functions are used as part of initialization, during diff --git a/clang/include/clang/AST/ASTContext.h b/clang/include/clang/AST/ASTContext.h index 08f71051e6cbf..86236e92dce49 100644 --- a/clang/include/clang/AST/ASTContext.h +++ b/clang/include/clang/AST/ASTContext.h @@ -1,5 +1,7 @@ //===- ASTContext.h - Context to hold long-lived AST nodes ------*- C++ -*-===// // +// Copyright 2024 Bloomberg Finance L.P. +// // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception @@ -235,6 +237,8 @@ class ASTContext : public RefCountedBase { ASTContext&> DependentTemplateSpecializationTypes; llvm::FoldingSet PackExpansionTypes; + mutable llvm::FoldingSet + DependentReflectionSpliceTypes; mutable llvm::FoldingSet ObjCObjectTypes; mutable llvm::FoldingSet ObjCObjectPointerTypes; mutable llvm::FoldingSet @@ -1116,6 +1120,7 @@ class ASTContext : public RefCountedBase { CanQualType BFloat16Ty; CanQualType Float16Ty; // C11 extension ISO/IEC TS 18661-3 CanQualType VoidPtrTy, NullPtrTy; + CanQualType MetaInfoTy; CanQualType DependentTy, OverloadTy, BoundMemberTy, UnknownAnyTy; CanQualType BuiltinFnTy; CanQualType PseudoObjectTy, ARCUnbridgedCastTy; @@ -1692,6 +1697,8 @@ class ASTContext : public RefCountedBase { std::optional NumExpansions, bool ExpectPackInType = true); + QualType getReflectionSpliceType(Expr *E, QualType UnderlyingType) const; + QualType getObjCInterfaceType(const ObjCInterfaceDecl *Decl, ObjCInterfaceDecl *PrevDecl = nullptr) const; @@ -2217,6 +2224,7 @@ class ASTContext : public RefCountedBase { const IdentifierInfo *Name) const; TemplateName getDependentTemplateName(NestedNameSpecifier *NNS, OverloadedOperatorKind Operator) const; + TemplateName getDependentTemplateName(CXXIndeterminateSpliceExpr *S) const; TemplateName getSubstTemplateTemplateParm(TemplateName replacement, Decl *AssociatedDecl, unsigned Index, diff --git a/clang/include/clang/AST/AbstractBasicReader.h b/clang/include/clang/AST/AbstractBasicReader.h index 1f2797cc70145..4ea5dccd7314f 100644 --- a/clang/include/clang/AST/AbstractBasicReader.h +++ b/clang/include/clang/AST/AbstractBasicReader.h @@ -177,6 +177,10 @@ class DataStreamBasicReader : public BasicReaderBase { return llvm::APInt(bitWidth, numWords, &data[0]); } + ReflectionValue readReflectionValue() { + llvm_unreachable("unimplemented"); + } + llvm::FixedPointSemantics readFixedPointSemantics() { unsigned width = asImpl().readUInt32(); unsigned scale = asImpl().readUInt32(); @@ -284,6 +288,13 @@ class DataStreamBasicReader : public BasicReaderBase { cur = NestedNameSpecifier::SuperSpecifier(ctx, asImpl().readCXXRecordDeclRef()); continue; + + case NestedNameSpecifier::IndeterminateSplice: + cur = NestedNameSpecifier::IndeterminateSpliceSpecifier( + ctx, + reinterpret_cast( + asImpl().readExprRef())); + continue; } llvm_unreachable("bad nested name specifier kind"); } diff --git a/clang/include/clang/AST/AbstractBasicWriter.h b/clang/include/clang/AST/AbstractBasicWriter.h index 07afa388de2c1..5fcb11f65e3fd 100644 --- a/clang/include/clang/AST/AbstractBasicWriter.h +++ b/clang/include/clang/AST/AbstractBasicWriter.h @@ -263,6 +263,10 @@ class DataStreamBasicWriter : public BasicWriterBase { case NestedNameSpecifier::Super: asImpl().writeDeclRef(NNS->getAsRecordDecl()); continue; + + case NestedNameSpecifier::IndeterminateSplice: + asImpl().writeExprRef(NNS->getAsSpliceExpr()); + continue; } llvm_unreachable("bad nested name specifier kind"); } diff --git a/clang/include/clang/AST/BuiltinTypes.def b/clang/include/clang/AST/BuiltinTypes.def index c04f6f6f12719..5d5af9e1f0cb8 100644 --- a/clang/include/clang/AST/BuiltinTypes.def +++ b/clang/include/clang/AST/BuiltinTypes.def @@ -1,5 +1,7 @@ //===-- BuiltinTypes.def - Metadata about BuiltinTypes ----------*- C++ -*-===// // +// Copyright 2024 Bloomberg Finance L.P. +// // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception @@ -226,6 +228,10 @@ FLOATING_TYPE(Ibm128, Ibm128Ty) // This is the type of C++0x 'nullptr'. BUILTIN_TYPE(NullPtr, NullPtrTy) +// This is this type proposed for C++2c by P2996 for representing +// internal compiler objects reflected by a value. +BUILTIN_TYPE(MetaInfo, MetaInfoTy) + // The primitive Objective C 'id' type. The user-visible 'id' // type is a typedef of an ObjCObjectPointerType to an // ObjCObjectType with this as its base. In fact, this only ever diff --git a/clang/include/clang/AST/ComputeDependence.h b/clang/include/clang/AST/ComputeDependence.h index 7abf9141237dc..8027e863d09cf 100644 --- a/clang/include/clang/AST/ComputeDependence.h +++ b/clang/include/clang/AST/ComputeDependence.h @@ -1,5 +1,7 @@ //===--- ComputeDependence.h -------------------------------------- C++ -*-===// // +// Copyright 2024 Bloomberg Finance L.P. +// // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception @@ -94,6 +96,13 @@ class DesignatedInitExpr; class ParenListExpr; class PseudoObjectExpr; class AtomicExpr; +class CXXReflectExpr; +class CXXMetafunctionExpr; +class CXXIndeterminateSpliceExpr; +class CXXExprSpliceExpr; +class CXXDependentMemberSpliceExpr; +class StackLocationExpr; +class ValueOfLValueExpr; class OMPArraySectionExpr; class OMPArrayShapingExpr; class OMPIteratorExpr; @@ -189,6 +198,14 @@ ExprDependence computeDependence(ParenListExpr *E); ExprDependence computeDependence(PseudoObjectExpr *E); ExprDependence computeDependence(AtomicExpr *E); +ExprDependence computeDependence(CXXReflectExpr *E, const ASTContext &C); +ExprDependence computeDependence(CXXMetafunctionExpr *E); +ExprDependence computeDependence(CXXIndeterminateSpliceExpr *E); +ExprDependence computeDependence(CXXExprSpliceExpr *E); +ExprDependence computeDependence(CXXDependentMemberSpliceExpr *E); +ExprDependence computeDependence(StackLocationExpr *E); +ExprDependence computeDependence(ValueOfLValueExpr *E); + ExprDependence computeDependence(OMPArraySectionExpr *E); ExprDependence computeDependence(OMPArrayShapingExpr *E); ExprDependence computeDependence(OMPIteratorExpr *E); diff --git a/clang/include/clang/AST/Decl.h b/clang/include/clang/AST/Decl.h index 5f1f83bb00282..4220bfeff2b1e 100644 --- a/clang/include/clang/AST/Decl.h +++ b/clang/include/clang/AST/Decl.h @@ -562,10 +562,12 @@ class NamespaceDecl : public NamedDecl, public DeclContext, llvm::PointerIntPair AnonOrFirstNamespaceAndFlags; - NamespaceDecl(ASTContext &C, DeclContext *DC, bool Inline, +protected: + NamespaceDecl(Kind K, ASTContext &C, DeclContext *DC, bool Inline, SourceLocation StartLoc, SourceLocation IdLoc, IdentifierInfo *Id, NamespaceDecl *PrevDecl, bool Nested); +private: using redeclarable_base = Redeclarable; NamespaceDecl *getNextRedeclarationImpl() override; @@ -689,7 +691,9 @@ class NamespaceDecl : public NamedDecl, public DeclContext, // Implement isa/cast/dyncast/etc. static bool classof(const Decl *D) { return classofKind(D->getKind()); } - static bool classofKind(Kind K) { return K == Namespace; } + static bool classofKind(Kind K) { + return K >= firstNamespace && K <= lastNamespace; + } static DeclContext *castToDeclContext(const NamespaceDecl *D) { return static_cast(const_cast(D)); } @@ -4296,6 +4300,10 @@ class RecordDecl : public TagDecl { void setIsRandomized(bool V) { RecordDeclBits.IsRandomized = V; } + bool isMetaType() const { return RecordDeclBits.IsMetaType; } + + void setIsMetaType(bool V) { RecordDeclBits.IsMetaType = V; } + void reorderDecls(const SmallVectorImpl &Decls); /// Determines whether this declaration represents the diff --git a/clang/include/clang/AST/DeclBase.h b/clang/include/clang/AST/DeclBase.h index 858450926455c..67d9bbf519881 100644 --- a/clang/include/clang/AST/DeclBase.h +++ b/clang/include/clang/AST/DeclBase.h @@ -1668,9 +1668,13 @@ class DeclContext { LLVM_PREFERRED_TYPE(bool) uint64_t IsRandomized : 1; + /// Indicates whether this struct is a meta type. + LLVM_PREFERRED_TYPE(bool) + uint64_t IsMetaType : 1; + /// True if a valid hash is stored in ODRHash. This should shave off some /// extra storage and prevent CXXRecordDecl to store unused bits. - uint64_t ODRHash : 26; + uint64_t ODRHash : 25; }; /// Number of inherited and non-inherited bits in RecordDeclBitfields. diff --git a/clang/include/clang/AST/DeclCXX.h b/clang/include/clang/AST/DeclCXX.h index 7aed4d5cbc002..1b261991d4d8c 100644 --- a/clang/include/clang/AST/DeclCXX.h +++ b/clang/include/clang/AST/DeclCXX.h @@ -61,8 +61,10 @@ class CXXBasePaths; class CXXConstructorDecl; class CXXDestructorDecl; class CXXFinalOverriderMap; +class CXXIndeterminateSpliceExpr; class CXXIndirectPrimaryBaseSet; class CXXMethodDecl; +class CXXRecordDecl; class DecompositionDecl; class FriendDecl; class FunctionTemplateDecl; @@ -157,13 +159,6 @@ class CXXBaseSpecifier { LLVM_PREFERRED_TYPE(bool) unsigned Virtual : 1; - /// Whether this is the base of a class (true) or of a struct (false). - /// - /// This determines the mapping from the access specifier as written in the - /// source code to the access specifier used for semantic analysis. - LLVM_PREFERRED_TYPE(bool) - unsigned BaseOfClass : 1; - /// Access specifier as written in the source code (may be AS_none). /// /// The actual type of data stored here is an AccessSpecifier, but we use @@ -182,12 +177,16 @@ class CXXBaseSpecifier { /// range does not include the \c virtual or the access specifier. TypeSourceInfo *BaseTypeInfo; + /// The derived record type that this base specifier applies to. + CXXRecordDecl *Derived; + public: CXXBaseSpecifier() = default; - CXXBaseSpecifier(SourceRange R, bool V, bool BC, AccessSpecifier A, - TypeSourceInfo *TInfo, SourceLocation EllipsisLoc) - : Range(R), EllipsisLoc(EllipsisLoc), Virtual(V), BaseOfClass(BC), - Access(A), InheritConstructors(false), BaseTypeInfo(TInfo) {} + CXXBaseSpecifier(SourceRange R, bool V, AccessSpecifier A, + TypeSourceInfo *TInfo, CXXRecordDecl *D, + SourceLocation EllipsisLoc) + : Range(R), EllipsisLoc(EllipsisLoc), Virtual(V), Access(A), + InheritConstructors(false), BaseTypeInfo(TInfo), Derived(D) {} /// Retrieves the source range that contains the entire base specifier. SourceRange getSourceRange() const LLVM_READONLY { return Range; } @@ -204,7 +203,9 @@ class CXXBaseSpecifier { /// Determine whether this base class is a base of a class declared /// with the 'class' keyword (vs. one declared with the 'struct' keyword). - bool isBaseOfClass() const { return BaseOfClass; } + bool isBaseOfClass() const { + return dyn_cast(Derived)->isClass(); + } /// Determine whether this base specifier is a pack expansion. bool isPackExpansion() const { return EllipsisLoc.isValid(); } @@ -229,7 +230,7 @@ class CXXBaseSpecifier { /// written in the source code, use getAccessSpecifierAsWritten(). AccessSpecifier getAccessSpecifier() const { if ((AccessSpecifier)Access == AS_none) - return BaseOfClass? AS_private : AS_public; + return isBaseOfClass()? AS_private : AS_public; else return (AccessSpecifier)Access; } @@ -250,6 +251,10 @@ class CXXBaseSpecifier { return BaseTypeInfo->getType().getUnqualifiedType(); } + CXXRecordDecl *getDerived() const { return Derived; } + + void setDerived(CXXRecordDecl *D) { Derived = D; } + /// Retrieves the type and source location of the base class. TypeSourceInfo *getTypeSourceInfo() const { return BaseTypeInfo; } }; @@ -3106,6 +3111,30 @@ class UsingDirectiveDecl : public NamedDecl { static bool classofKind(Kind K) { return K == UsingDirective; } }; +class DependentNamespaceDecl : public NamespaceDecl { + friend class ASTDeclReader; + + CXXIndeterminateSpliceExpr *SpliceExpr; + + DependentNamespaceDecl(ASTContext &C, DeclContext *DC, + CXXIndeterminateSpliceExpr *SpliceExpr); + + void anchor() override; + +public: + static DependentNamespaceDecl *Create(ASTContext &C, DeclContext *DC, + CXXIndeterminateSpliceExpr *SpliceExpr); + + static DependentNamespaceDecl *CreateDeserialized(ASTContext &C, unsigned ID); + + CXXIndeterminateSpliceExpr *getSpliceExpr() const { return SpliceExpr; } + + SourceRange getSourceRange() const override LLVM_READONLY; + + static bool classof(const Decl *D) { return classofKind(D->getKind()); } + static bool classofKind(Kind K) { return K == DependentNamespace; } +}; + /// Represents a C++ namespace alias. /// /// For example: @@ -3197,6 +3226,17 @@ class NamespaceAliasDecl : public NamedDecl, return const_cast(this)->getNamespace(); } + bool isDependent() const { + if (NestedNameSpecifier *Qualifier = getQualifier(); + Qualifier && Qualifier->isDependent()) + return true; + + if (auto *AD = dyn_cast(Namespace)) + return AD->isDependent(); + + return isa(Namespace); + } + /// Returns the location of the alias name, i.e. 'foo' in /// "namespace foo = ns::bar;". SourceLocation getAliasLoc() const { return getLocation(); } diff --git a/clang/include/clang/AST/DeclTemplate.h b/clang/include/clang/AST/DeclTemplate.h index e3b6a7efb1127..3acbd9f47007a 100644 --- a/clang/include/clang/AST/DeclTemplate.h +++ b/clang/include/clang/AST/DeclTemplate.h @@ -2715,6 +2715,8 @@ class VarTemplateSpecializationDecl : public VarDecl, PointOfInstantiation = Loc; } + bool isCompleteDefinition() { return IsCompleteDefinition; } + void setCompleteDefinition() { IsCompleteDefinition = true; } /// If this variable template specialization is an instantiation of diff --git a/clang/include/clang/AST/DependenceFlags.h b/clang/include/clang/AST/DependenceFlags.h index 3b3c1afb096ad..860ede3acdbe2 100644 --- a/clang/include/clang/AST/DependenceFlags.h +++ b/clang/include/clang/AST/DependenceFlags.h @@ -297,6 +297,11 @@ toNestedNameSpecifierDependendence(TypeDependence D) { return Dependence(D).nestedNameSpecifier(); } +inline NestedNameSpecifierDependence +toNestedNameSpecifierDependence(ExprDependence E) { + return Dependence(E).nestedNameSpecifier(); +} + inline TemplateArgumentDependence toTemplateArgumentDependence(TypeDependence D) { return Dependence(D).templateArgument(); diff --git a/clang/include/clang/AST/Expr.h b/clang/include/clang/AST/Expr.h index 2bfefeabc348b..aa7c6cdc8789f 100644 --- a/clang/include/clang/AST/Expr.h +++ b/clang/include/clang/AST/Expr.h @@ -1,5 +1,7 @@ //===--- Expr.h - Classes for representing expressions ----------*- C++ -*-===// // +// Copyright 2024 Bloomberg Finance L.P. +// // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception @@ -928,6 +930,14 @@ class Expr : public ValueStmt { return const_cast(this)->IgnoreParenCasts(); } + /// Skip past any expressions splices which might surround this expression + /// until reaching a fixed point. Skips: + /// * CXXExprSpliceExpr + Expr *IgnoreExprSplices() LLVM_READONLY; + const Expr *IgnoreExprSplices() const { + return const_cast(this)->IgnoreExprSplices(); + } + /// Skip conversion operators. If this Expr is a call to a conversion /// operator, return the argument. Expr *IgnoreConversionOperatorSingleStep() LLVM_READONLY; @@ -1047,10 +1057,12 @@ class FullExpr : public Expr { setDependence(computeDependence(this)); } FullExpr(StmtClass SC, EmptyShell Empty) - : Expr(SC, Empty) {} + : Expr(SC, Empty), SubExpr(nullptr) { + setDependence(ExprDependence::None); + } public: - const Expr *getSubExpr() const { return cast(SubExpr); } - Expr *getSubExpr() { return cast(SubExpr); } + const Expr *getSubExpr() const { return SubExpr ? cast(SubExpr) : nullptr; } + Expr *getSubExpr() { return SubExpr ? cast(SubExpr) : nullptr; } /// As with any mutator of the AST, be very careful when modifying an /// existing AST to preserve its invariants. @@ -1120,10 +1132,10 @@ class ConstantExpr final const ASTContext &Context); SourceLocation getBeginLoc() const LLVM_READONLY { - return SubExpr->getBeginLoc(); + return SubExpr ? SubExpr->getBeginLoc() : SourceLocation(); } SourceLocation getEndLoc() const LLVM_READONLY { - return SubExpr->getEndLoc(); + return SubExpr ? SubExpr->getEndLoc() : SourceLocation(); } static bool classof(const Stmt *T) { diff --git a/clang/include/clang/AST/ExprCXX.h b/clang/include/clang/AST/ExprCXX.h index 6003b866c9f56..3d856cfa9947a 100644 --- a/clang/include/clang/AST/ExprCXX.h +++ b/clang/include/clang/AST/ExprCXX.h @@ -1,5 +1,7 @@ //===- ExprCXX.h - Classes for representing expressions ---------*- C++ -*-===// // +// Copyright 2024 Bloomberg Finance L.P. +// // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception @@ -25,6 +27,7 @@ #include "clang/AST/Expr.h" #include "clang/AST/NestedNameSpecifier.h" #include "clang/AST/OperationKinds.h" +#include "clang/AST/Reflection.h" #include "clang/AST/Stmt.h" #include "clang/AST/StmtCXX.h" #include "clang/AST/TemplateBase.h" @@ -49,6 +52,7 @@ #include #include #include +#include #include #include @@ -5274,6 +5278,429 @@ class BuiltinBitCastExpr final } }; +/// Represents a C++2c reflect expression (P2996). The operand of the expression +/// is either a type, an expression, a template-name, or a namespace. +class CXXReflectExpr : public Expr { + // The operand of the expression. + ReflectionValue Ref; + + // Source locations. + SourceLocation OpLoc; + SourceLocation ArgLoc; + + CXXReflectExpr(const ASTContext &C, QualType T, QualType Arg); + CXXReflectExpr(const ASTContext &C, QualType T, Expr *Arg); + CXXReflectExpr(const ASTContext &C, QualType T, Decl *Arg, bool IsNamespace); + CXXReflectExpr(const ASTContext &C, QualType T, TemplateName Arg); + CXXReflectExpr(const ASTContext &C, QualType T, CXXBaseSpecifier *Arg); + CXXReflectExpr(const ASTContext &C, QualType T, TagDataMemberSpec *Arg); + +public: + static CXXReflectExpr *Create(ASTContext &C, SourceLocation OperatorLoc, + SourceLocation ArgLoc, QualType Operand); + static CXXReflectExpr *Create(ASTContext &C, SourceLocation OperatorLoc, + Expr *Operand); + static CXXReflectExpr *Create(ASTContext &C, SourceLocation OperatorLoc, + SourceLocation OperandLoc, Decl *Operand); + static CXXReflectExpr *Create(ASTContext &C, SourceLocation OperatorLoc, + SourceLocation OperandLoc, + const TemplateName Operand); + static CXXReflectExpr *Create(ASTContext &C, SourceLocation OperatorLoc, + SourceLocation OperandLoc, + CXXBaseSpecifier *Operand); + static CXXReflectExpr *Create(ASTContext &C, SourceLocation OperatorLoc, + SourceLocation OperandLoc, + TagDataMemberSpec *Arg); + + /// Returns the operand of the reflection expression. + const ReflectionValue &getOperand() const { return Ref; } + + SourceLocation getBeginLoc() const LLVM_READONLY { return OpLoc; } + SourceLocation getEndLoc() const LLVM_READONLY { return ArgLoc; } + + /// Returns location of the '^'-operator. + SourceLocation getOperatorLoc() const { return OpLoc; } + SourceLocation getArgLoc() const { return ArgLoc; } + + /// Sets the location of the '^'-operator. + void setOperatorLoc(SourceLocation L) { OpLoc = L; } + void setArgLoc(SourceLocation L) { ArgLoc = L; } + + child_range children() { + return child_range(child_iterator(), child_iterator()); + } + + const_child_range children() const { + return const_child_range(const_child_iterator(), const_child_iterator()); + } + + static bool classof(const Stmt *T) { + return T->getStmtClass() == CXXReflectExprClass; + } +}; + +/// Represents a C++2c "metafunction", a function that operates on one or more +/// reflections (P2996). Arguments vary by function. +class CXXMetafunctionExpr : public Expr { +public: + // Type of callback provided to executing metafunctinons to help evaluate an + // expression in the current constant evaluation context. + using EvaluateFn = std::function; + + // Type of callback used to evaluate the metafunction during constant + // evaluation. This will be a lambda with the bound 'Sema' object. + using ImplFn = std::function)>; + +private: + + // The original ID of the corresponding metafunction. Needed to re-create the + // expression during Tree Transform. + unsigned MetaFnID; + + // An unowned reference to a callback for executing the metafunction at + // constant evaluation time. + const ImplFn &Impl; + + // Result type. + QualType ResultType; + + // Arguments. + unsigned NumArgs; + Expr **Args; + + // Source locations. + SourceLocation KwLoc; + SourceLocation LParenLoc; + SourceLocation RParenLoc; + + CXXMetafunctionExpr(unsigned MetaFnID, const ImplFn &Impl, + QualType ResultType, ExprValueKind VK, Expr ** Args, + unsigned NumArgs, SourceLocation KwLoc, + SourceLocation LParenLoc, SourceLocation RParenLoc); +public: + static CXXMetafunctionExpr *Create(ASTContext &C, unsigned MetaFnID, + const ImplFn &Impl, + QualType ResultType, + ArrayRef Args, + SourceLocation KwLoc, + SourceLocation LParenLoc, + SourceLocation RParenLoc); + + unsigned getMetaFnID() const { + return MetaFnID; + } + + const ImplFn &getImpl() const { + return Impl; + } + + QualType getResultType() const { + return ResultType; + } + + unsigned getNumArgs() const { + return NumArgs; + } + + Expr *getArg(unsigned I) const { + assert(I < NumArgs && "argument out-of-range"); + return cast(Args[I]); + } + + SourceLocation getKwLoc() const { + return KwLoc; + } + + SourceLocation getLParenLoc() const { + return LParenLoc; + } + + SourceLocation getRParenLoc() const { + return RParenLoc; + } + + SourceLocation getBeginLoc() const { + return KwLoc; + } + + SourceLocation getEndLoc() const { + return RParenLoc; + } + + SourceRange getSourceRange() const { + return SourceRange(getBeginLoc(), getEndLoc()); + } + + child_range children() { + return child_range(reinterpret_cast(&Args[0]), + reinterpret_cast(&Args[NumArgs])); + } + + const_child_range children() const { + return const_child_range(reinterpret_cast(&Args[0]), + reinterpret_cast(&Args[NumArgs])); + } + + static bool classof(const Stmt *T) { + return T->getStmtClass() == CXXMetafunctionExprClass; + } +}; + +/// Represents a C++2c splice "expression". Strictly speaking, it may be a mild +/// abuse of terminology to classify a splice as an expression, since it can +/// yield a type, namespace, or template-id in addition to a value. That said, +/// the -operand- of a splice is an expression (which always evaluates to a +/// type context-convertible to 'std::meta::info'), and 'Expr' provides a +/// convenient existing means of storing the SourceLocation of the splice +/// operands alongside the operand. We therefore consider a "[:R:]" a "splice +/// expression", and treat a "splice" as an operation occupying the same source +/// range as the splice expression. +class CXXIndeterminateSpliceExpr : public Expr { + SourceLocation LSpliceLoc; + Expr *Operand; + SourceLocation RSpliceLoc; + + CXXIndeterminateSpliceExpr(QualType ResultTy, SourceLocation LSpliceLoc, + Expr *Operand, SourceLocation RSpliceLoc); + +public: + static CXXIndeterminateSpliceExpr *Create(ASTContext &C, + SourceLocation LSpliceLoc, + Expr *Operand, + SourceLocation RSpliceLoc); + + Expr *getOperand() const { + return Operand; + } + + SourceLocation getLSpliceLoc() const { + return LSpliceLoc; + } + + SourceLocation getRSpliceLoc() const { + return RSpliceLoc; + } + + SourceLocation getBeginLoc() const { + return LSpliceLoc; + } + + SourceLocation getEndLoc() const { + return RSpliceLoc; + } + + SourceRange getSourceRange() const { + return SourceRange(getBeginLoc(), getEndLoc()); + } + + child_range children() { + return child_range(reinterpret_cast(&Operand), + reinterpret_cast(&Operand) + 1); + } + + const_child_range children() const { + return const_child_range( + reinterpret_cast(const_cast(&Operand)), + reinterpret_cast(const_cast(&Operand) + 1)); + } + + static bool classof(const Stmt *T) { + return T->getStmtClass() == CXXIndeterminateSpliceExprClass; + } +}; + +// Implementation detail of the 'is_accessible' metafunction. +// Used to "reach up the stack" to find the context from which the metafunction +// was called, such that the accessibility of a class member can thereafter be +// evaluated from that context. +class StackLocationExpr : public Expr { + SourceRange Range; + int FrameOffset; + + StackLocationExpr(QualType ResultTy, SourceRange Range, int FrameOffset); + +public: + static StackLocationExpr *Create(ASTContext &C, SourceRange Range, + int FrameOffset); + + int getFrameOffset() const { + return FrameOffset; + } + + SourceLocation getBeginLoc() const { + return Range.getBegin(); + } + + SourceLocation getEndLoc() const { + return Range.getEnd(); + } + + child_range children() { + return child_range(child_iterator(), child_iterator()); + } + + const_child_range children() const { + return const_child_range(const_child_iterator(), const_child_iterator()); + } + + static bool classof(const Stmt *T) { + return T->getStmtClass() == StackLocationExprClass; + } +}; + +// Implementation detail of the 'value_of' metafunction. +// Used to "reach up the stack" of a constant evaluation to obtain the "most +// recent LValue" associated with a particular variable. +class ValueOfLValueExpr : public Expr { + SourceRange Range; + ValueDecl *Decl; + + ValueOfLValueExpr(QualType ResultTy, SourceRange Range, ValueDecl *VD); + +public: + static ValueOfLValueExpr *Create(ASTContext &C, SourceRange Range, + QualType ResultTy, ValueDecl *VD); + + ValueDecl *getValueDecl() const { + return Decl; + } + + SourceLocation getBeginLoc() const { + return Range.getBegin(); + } + + SourceLocation getEndLoc() const { + return Range.getEnd(); + } + + child_range children() { + return child_range(child_iterator(), child_iterator()); + } + + const_child_range children() const { + return const_child_range(const_child_iterator(), const_child_iterator()); + } + + static bool classof(const Stmt *T) { + return T->getStmtClass() == StackLocationExprClass; + } +}; + +class CXXExprSpliceExpr : public Expr { + SourceLocation LSpliceLoc; + Expr *Operand; + SourceLocation RSpliceLoc; + bool AllowMemberReference; + + CXXExprSpliceExpr(QualType ResultTy, ExprValueKind ValueKind, + SourceLocation LSpliceLoc, Expr *Operand, + SourceLocation RSpliceLoc, bool AllowMemberReference); + +public: + static CXXExprSpliceExpr *Create(ASTContext &C, ExprValueKind ValueKind, + SourceLocation LSpliceLoc, Expr *Operand, + SourceLocation RSpliceLoc, + bool AllowMemberReference); + + Expr *getOperand() const { + return Operand; + } + + bool allowMemberReference() const { + return AllowMemberReference; + } + + SourceLocation getLSpliceLoc() const { + return LSpliceLoc; + } + + SourceLocation getRSpliceLoc() const { + return RSpliceLoc; + } + + SourceLocation getBeginLoc() const { + return LSpliceLoc; + } + + SourceLocation getEndLoc() const { + return RSpliceLoc; + } + + child_range children() { + return child_range(reinterpret_cast(&Operand), + reinterpret_cast(&Operand) + 1); + } + + const_child_range children() const { + return const_child_range( + reinterpret_cast(const_cast(&Operand)), + reinterpret_cast(const_cast(&Operand) + 1)); + } + + static bool classof(const Stmt *T) { + return T->getStmtClass() == CXXExprSpliceExprClass; + } +}; + +// Represents a member access expression having an expression splice following +// the member access operator (e.g., 'a.[:r:]') for which the expression splice +// is dependent on a template. This expression will be rewritten as a MemberExpr +// (or an UnresolvedMemberExpr) during Tree Transform. +class CXXDependentMemberSpliceExpr : public Expr { + Stmt *SubExprs[2]; + + SourceLocation OpLoc; + bool IsArrow; + + CXXDependentMemberSpliceExpr(QualType ResultTy, Expr *Base, + SourceLocation OpLoc, bool IsArrow, + CXXExprSpliceExpr *RHS); + +public: + static CXXDependentMemberSpliceExpr *Create(ASTContext &C, Expr *Base, + SourceLocation OpLoc, + bool IsArrow, + CXXExprSpliceExpr *RHS); + + Expr *getBase() const { + return cast(SubExprs[0]); + } + + SourceLocation getOpLoc() const { + return OpLoc; + } + + bool isArrow() const { + return IsArrow; + } + + CXXExprSpliceExpr *getRHS() const { + return cast(SubExprs[1]); + } + + SourceLocation getBeginLoc() const { + return getBase()->getBeginLoc(); + } + + SourceLocation getEndLoc() const { + return getRHS()->getEndLoc(); + } + + child_range children() { + return child_range(SubExprs, SubExprs + 2); + } + + const_child_range children() const { + return const_child_range(SubExprs, SubExprs + 2); + } + + static bool classof(const Stmt *T) { + return T->getStmtClass() == CXXDependentMemberSpliceExprClass; + } +}; + } // namespace clang #endif // LLVM_CLANG_AST_EXPRCXX_H diff --git a/clang/include/clang/AST/IgnoreExpr.h b/clang/include/clang/AST/IgnoreExpr.h index 917bada61fa6f..697ce0a2c4c6b 100644 --- a/clang/include/clang/AST/IgnoreExpr.h +++ b/clang/include/clang/AST/IgnoreExpr.h @@ -1,5 +1,7 @@ //===--- IgnoreExpr.h - Ignore intermediate Expressions -----------------===// // +// Copyright 2024 Bloomberg Finance L.P. +// // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception @@ -49,8 +51,9 @@ inline Expr *IgnoreImplicitCastsSingleStep(Expr *E) { if (auto *ICE = dyn_cast(E)) return ICE->getSubExpr(); - if (auto *FE = dyn_cast(E)) - return FE->getSubExpr(); + if (auto *FE = dyn_cast(E)) { + return FE->getSubExpr() ? FE->getSubExpr() : E; + } return E; } @@ -77,7 +80,7 @@ inline Expr *IgnoreCastsSingleStep(Expr *E) { return CE->getSubExpr(); if (auto *FE = dyn_cast(E)) - return FE->getSubExpr(); + return FE->getSubExpr() ? FE->getSubExpr() : E; if (auto *MTE = dyn_cast(E)) return MTE->getSubExpr(); @@ -147,6 +150,12 @@ inline Expr *IgnoreParensOnlySingleStep(Expr *E) { return E; } +inline Expr *IgnoreExprSpliceSingleStep(Expr *E) { + if (auto *ESE = dyn_cast(E)) + return ESE->getOperand(); + return E; +} + inline Expr *IgnoreParensSingleStep(Expr *E) { if (auto *PE = dyn_cast(E)) return PE->getSubExpr(); diff --git a/clang/include/clang/AST/LocInfoType.h b/clang/include/clang/AST/LocInfoType.h index 876c7deeceb9a..61ab0d53e5d79 100644 --- a/clang/include/clang/AST/LocInfoType.h +++ b/clang/include/clang/AST/LocInfoType.h @@ -35,7 +35,8 @@ class LocInfoType : public Type { TypeSourceInfo *DeclInfo; LocInfoType(QualType ty, TypeSourceInfo *TInfo) - : Type((TypeClass)LocInfo, ty, ty->getDependence()), DeclInfo(TInfo) { + : Type((TypeClass)LocInfo, ty, ty->getDependence(), ty->isMetaType()), + DeclInfo(TInfo) { assert(getTypeClass() == (TypeClass)LocInfo && "LocInfo didn't fit in TC?"); } friend class Sema; diff --git a/clang/include/clang/AST/NestedNameSpecifier.h b/clang/include/clang/AST/NestedNameSpecifier.h index 3b6cf97211850..77641a2cd0272 100644 --- a/clang/include/clang/AST/NestedNameSpecifier.h +++ b/clang/include/clang/AST/NestedNameSpecifier.h @@ -1,5 +1,7 @@ //===- NestedNameSpecifier.h - C++ nested name specifiers -------*- C++ -*-===// // +// Copyright 2024 Bloomberg Finance L.P. +// // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception @@ -29,6 +31,7 @@ namespace clang { class ASTContext; class CXXRecordDecl; +class CXXIndeterminateSpliceExpr; class IdentifierInfo; class LangOptions; class NamespaceAliasDecl; @@ -44,8 +47,9 @@ class TypeLoc; /// names. For example, "foo::" in "foo::x" is a nested name /// specifier. Nested name specifiers are made up of a sequence of /// specifiers, each of which can be a namespace, type, identifier -/// (for dependent names), decltype specifier, or the global specifier ('::'). -/// The last two specifiers can only appear at the start of a +/// (for dependent names), decltype specifier, expression (for +/// indeterminate splices), or the global specifier ('::'). +/// The last three specifiers can only appear at the start of a /// nested-namespace-specifier. class NestedNameSpecifier : public llvm::FoldingSetNode { /// Enumeration describing @@ -53,16 +57,17 @@ class NestedNameSpecifier : public llvm::FoldingSetNode { StoredIdentifier = 0, StoredDecl = 1, StoredTypeSpec = 2, - StoredTypeSpecWithTemplate = 3 + StoredTypeSpecWithTemplate = 3, + StoredIndeterminateSplice = 4 }; /// The nested name specifier that precedes this nested name /// specifier. /// /// The pointer is the nested-name-specifier that precedes this - /// one. The integer stores one of the first four values of type + /// one. The integer stores one of the first five values of type /// SpecifierKind. - llvm::PointerIntPair Prefix; + llvm::PointerIntPair Prefix; /// The last component in the nested name specifier, which /// can be an identifier, a declaration, or a type. @@ -98,7 +103,11 @@ class NestedNameSpecifier : public llvm::FoldingSetNode { /// Microsoft's '__super' specifier, stored as a CXXRecordDecl* of /// the class it appeared in. - Super + Super, + + /// A reflection splice of indeterminate kind, stored as an + /// CXXIndeterminateSpliceExpr*. + IndeterminateSplice, }; private: @@ -159,6 +168,10 @@ class NestedNameSpecifier : public llvm::FoldingSetNode { static NestedNameSpecifier *SuperSpecifier(const ASTContext &Context, CXXRecordDecl *RD); + /// Returns the nested name specifier representing an indeterminate splice. + static NestedNameSpecifier *IndeterminateSpliceSpecifier( + const ASTContext &Context, const CXXIndeterminateSpliceExpr *Expr); + /// Return the prefix of this nested name specifier. /// /// The prefix contains all of the parts of the nested name @@ -201,6 +214,14 @@ class NestedNameSpecifier : public llvm::FoldingSetNode { return nullptr; } + /// Retrieve the splice expression stored in this nested name specifier. + const CXXIndeterminateSpliceExpr *getAsSpliceExpr() const { + if (Prefix.getInt() == StoredIndeterminateSplice) + return (const CXXIndeterminateSpliceExpr *)Specifier; + + return nullptr; + } + NestedNameSpecifierDependence getDependence() const; /// Whether this nested name specifier refers to a dependent @@ -335,6 +356,10 @@ class NestedNameSpecifierLoc { /// retrieve the type with source-location information. TypeLoc getTypeLoc() const; + /// For a nested-name-specifier that refers to a splice expression, retrive + /// the expression. + const CXXIndeterminateSpliceExpr *getSpliceExpr() const; + /// Determines the data length for the entire /// nested-name-specifier. unsigned getDataLength() const { return getDataLength(Qualifier); } @@ -466,6 +491,19 @@ class NestedNameSpecifierLocBuilder { void MakeSuper(ASTContext &Context, CXXRecordDecl *RD, SourceLocation SuperLoc, SourceLocation ColonColonLoc); + /// Turns this (empty) nested-name-specifier into a specifier having a single + /// component of indeterminate splice kind. + /// + /// \param Context The AST context in which this nested-name-specifier + /// resides. + /// + /// \param Expr The indeterminate splice. + /// + /// \param ColonColonLoc The location of the trailing '::'. + void MakeIndeterminateSplice(ASTContext &Context, + const CXXIndeterminateSpliceExpr *Expr, + SourceLocation ColonColonLoc); + /// Make a new nested-name-specifier from incomplete source-location /// information. /// diff --git a/clang/include/clang/AST/PropertiesBase.td b/clang/include/clang/AST/PropertiesBase.td index 6df1d93a7ba2e..7743af2f2b150 100644 --- a/clang/include/clang/AST/PropertiesBase.td +++ b/clang/include/clang/AST/PropertiesBase.td @@ -1,5 +1,7 @@ //==--- PropertiesBase.td - Baseline definitions for AST properties -------===// // +// Copyright 2024 Bloomberg Finance L.P. +// // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception @@ -81,6 +83,8 @@ def Bool : PropertyType<"bool">; def BuiltinTypeKind : EnumPropertyType<"BuiltinType::Kind">; def BTFTypeTagAttr : PropertyType<"const BTFTypeTagAttr *">; def CallingConv : EnumPropertyType; +def CXXBaseSpecifierRef : + RefPropertyType<"CXXBaseSpecifier"> { let ConstWhenWriting = 1; } def DeclarationName : PropertyType; def DeclarationNameKind : EnumPropertyType<"DeclarationName::NameKind">; def DeclRef : RefPropertyType<"Decl"> { let ConstWhenWriting = 1; } @@ -130,10 +134,14 @@ def OverloadedOperatorKind : EnumPropertyType; def Qualifiers : PropertyType; def QualType : DefaultValuePropertyType; def RefQualifierKind : EnumPropertyType; +def ReflectionKind : EnumPropertyType<"ReflectionValue::ReflectionKind">; +def ReflectionValue : PropertyType { let PassByReference = 1; } def Selector : PropertyType; def SourceLocation : PropertyType; def StmtRef : RefPropertyType<"Stmt"> { let ConstWhenWriting = 1; } def ExprRef : SubclassPropertyType<"Expr", StmtRef>; +def TagDataMemberSpecRef : + RefPropertyType<"TagDataMemberSpec"> { let ConstWhenWriting = 1; } def TemplateArgument : PropertyType; def TemplateArgumentKind : EnumPropertyType<"TemplateArgument::ArgKind">; def TemplateName : DefaultValuePropertyType; @@ -252,6 +260,68 @@ class PropertyTypeCase : HasProperties { string Name = name; } +// Type cases for ReflectionValue. +def : PropertyTypeKind; +let Class = PropertyTypeCase in { + def : Property<"value", QualType> { + let Read = [{ node.getAsType() }]; + } + def : Creator<[{ + return ReflectionValue(ReflectionValue::RK_type, + value.getAsOpaquePtr()); + }]>; +} +let Class = PropertyTypeCase in { + def : Property<"value", StmtRef> { + let Read = [{ node.getAsConstValueExpr() }]; + } + def : Creator<[{ + return ReflectionValue(ReflectionValue::RK_const_value, value); + }]>; +} +let Class = PropertyTypeCase in { + def : Property<"value", ValueDeclRef> { + let Read = [{ node.getAsDecl() }]; + } + def : Creator<[{ + return ReflectionValue(ReflectionValue::RK_declaration, value); + }]>; +} +let Class = PropertyTypeCase in { + def : Property<"value", TemplateName> { + let Read = [{ node.getAsTemplate() }]; + } + def : Creator<[{ + return ReflectionValue(ReflectionValue::RK_template, + value.getAsTemplateDecl()); + }]>; +} +let Class = PropertyTypeCase in { + def : Property<"value", DeclRef> { + let Read = [{ node.getAsNamespace() }]; + } + def : Creator<[{ + return ReflectionValue(ReflectionValue::RK_namespace, value); + }]>; +} +let Class = PropertyTypeCase in { + def : Property<"value", CXXBaseSpecifierRef> { + let Read = [{ node.getAsBaseSpecifier() }]; + } + def : Creator<[{ + return ReflectionValue(ReflectionValue::RK_base_specifier, value); + }]>; +} +let Class = PropertyTypeCase in { + def : Property<"value", TagDataMemberSpecRef> { + let Read = [{ node.getAsDataMemberSpec() }]; + } + def : Creator<[{ + return ReflectionValue(ReflectionValue::RK_data_member_spec, value); + }]>; +} + // Type cases for APValue. def : PropertyTypeKind; @@ -424,6 +494,11 @@ let Class = PropertyTypeCase in { return APValue(cast(lhs), cast(rhs)); }]>; } +let Class = PropertyTypeCase in { + def : Creator<[{ + llvm_unreachable("unimplemented"); + }]>; +} let Class = PropertyTypeCase in { def : Property<"isDerived", Bool> { let Read = [{ node.isMemberPointerToDerivedMember() }]; @@ -809,6 +884,17 @@ let Class = PropertyTypeCase in { return TemplateArgument(ctx, value, type, isDefaulted); }]>; } +let Class = PropertyTypeCase in { + def : Property<"value", ReflectionValue> { + let Read = [{ node.getAsReflection() }]; + } + def : Property<"isDefaulted", Bool> { + let Read = [{ node.getIsDefaulted() }]; + } + def : Creator<[{ + return TemplateArgument(ctx, value, isDefaulted); + }]>; +} let Class = PropertyTypeCase in { def : Property<"value", APValue> { let Read = [{ node.getAsStructuralValue() }]; diff --git a/clang/include/clang/AST/RecursiveASTVisitor.h b/clang/include/clang/AST/RecursiveASTVisitor.h index 7eb92e304a385..b5d01c3ed095b 100644 --- a/clang/include/clang/AST/RecursiveASTVisitor.h +++ b/clang/include/clang/AST/RecursiveASTVisitor.h @@ -1,5 +1,7 @@ //===--- RecursiveASTVisitor.h - Recursive AST Visitor ----------*- C++ -*-===// // +// Copyright 2024 Bloomberg Finance L.P. +// // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception @@ -29,8 +31,10 @@ #include "clang/AST/ExprObjC.h" #include "clang/AST/ExprOpenMP.h" #include "clang/AST/LambdaCapture.h" +#include "clang/AST/LocInfoType.h" #include "clang/AST/NestedNameSpecifier.h" #include "clang/AST/OpenMPClause.h" +#include "clang/AST/Reflection.h" #include "clang/AST/Stmt.h" #include "clang/AST/StmtCXX.h" #include "clang/AST/StmtObjC.h" @@ -774,6 +778,11 @@ bool RecursiveASTVisitor::TraverseNestedNameSpecifier( case NestedNameSpecifier::Super: return true; + case NestedNameSpecifier::IndeterminateSplice: + TRY_TO(TraverseStmt( + const_cast(NNS->getAsSpliceExpr()))); + break; + case NestedNameSpecifier::TypeSpec: case NestedNameSpecifier::TypeSpecWithTemplate: TRY_TO(TraverseType(QualType(NNS->getAsType(), 0))); @@ -799,6 +808,11 @@ bool RecursiveASTVisitor::TraverseNestedNameSpecifierLoc( case NestedNameSpecifier::Super: return true; + case NestedNameSpecifier::IndeterminateSplice: + TRY_TO(TraverseStmt( + const_cast(NNS.getSpliceExpr()))); + break; + case NestedNameSpecifier::TypeSpec: case NestedNameSpecifier::TypeSpecWithTemplate: TRY_TO(TraverseTypeLoc(NNS.getTypeLoc())); @@ -854,10 +868,14 @@ bool RecursiveASTVisitor::TraverseTemplateArgument( case TemplateArgument::Null: case TemplateArgument::Declaration: case TemplateArgument::Integral: + case TemplateArgument::Reflection: case TemplateArgument::NullPtr: case TemplateArgument::StructuralValue: return true; + case TemplateArgument::IndeterminateSplice: + return getDerived().TraverseStmt(Arg.getAsIndeterminateSplice()); + case TemplateArgument::Type: return getDerived().TraverseType(Arg.getAsType()); @@ -887,10 +905,15 @@ bool RecursiveASTVisitor::TraverseTemplateArgumentLoc( case TemplateArgument::Null: case TemplateArgument::Declaration: case TemplateArgument::Integral: + case TemplateArgument::Reflection: case TemplateArgument::NullPtr: case TemplateArgument::StructuralValue: return true; + case TemplateArgument::IndeterminateSplice: + return getDerived().TraverseStmt( + ArgLoc.getSourceIndeterminateSpliceExpression()); + case TemplateArgument::Type: { // FIXME: how can TSI ever be NULL? if (TypeSourceInfo *TSI = ArgLoc.getTypeSourceInfo()) @@ -1076,6 +1099,9 @@ DEF_TRAVERSE_TYPE(TypeOfType, { TRY_TO(TraverseType(T->getUnmodifiedType())); }) DEF_TRAVERSE_TYPE(DecltypeType, { TRY_TO(TraverseStmt(T->getUnderlyingExpr())); }) +DEF_TRAVERSE_TYPE(ReflectionSpliceType, + { TRY_TO(TraverseStmt(T->getOperand())); }) + DEF_TRAVERSE_TYPE(PackIndexingType, { TRY_TO(TraverseType(T->getPattern())); TRY_TO(TraverseStmt(T->getIndexExpr())); @@ -1370,6 +1396,9 @@ DEF_TRAVERSE_TYPELOC(DecltypeType, { TRY_TO(TraverseStmt(TL.getTypePtr()->getUnderlyingExpr())); }) +DEF_TRAVERSE_TYPELOC(ReflectionSpliceType, + { TRY_TO(TraverseStmt(TL.getOperand())); }) + DEF_TRAVERSE_TYPELOC(PackIndexingType, { TRY_TO(TraverseType(TL.getPattern())); TRY_TO(TraverseStmt(TL.getTypePtr()->getIndexExpr())); @@ -1645,6 +1674,10 @@ DEF_TRAVERSE_DECL(NamespaceAliasDecl, { ShouldVisitChildren = false; }) +DEF_TRAVERSE_DECL(DependentNamespaceDecl, { + TRY_TO(TraverseStmt(D->getSpliceExpr())); +}) + DEF_TRAVERSE_DECL(LabelDecl, {// There is no code in a LabelDecl. }) @@ -2895,6 +2928,49 @@ DEF_TRAVERSE_STMT(SubstNonTypeTemplateParmExpr, {}) DEF_TRAVERSE_STMT(FunctionParmPackExpr, {}) DEF_TRAVERSE_STMT(CXXFoldExpr, {}) DEF_TRAVERSE_STMT(AtomicExpr, {}) +DEF_TRAVERSE_STMT(CXXReflectExpr, { + const ReflectionValue &Op = S->getOperand(); + switch (Op.getKind()) { + case ReflectionValue::RK_type: { + TRY_TO(TraverseType(Op.getAsType())); + break; + } + case ReflectionValue::RK_const_value: { + TRY_TO(TraverseStmt(Op.getAsConstValueExpr())); + break; + } + case ReflectionValue::RK_declaration: { + TRY_TO(TraverseDecl(Op.getAsDecl())); + break; + } + case ReflectionValue::RK_template: { + TRY_TO(TraverseTemplateName(Op.getAsTemplate())); + break; + } + case ReflectionValue::RK_namespace: { + TRY_TO(TraverseDecl(Op.getAsNamespace())); + break; + } + case ReflectionValue::RK_base_specifier: + case ReflectionValue::RK_data_member_spec: + break; + } +}) +DEF_TRAVERSE_STMT(CXXMetafunctionExpr, {}) +DEF_TRAVERSE_STMT(CXXIndeterminateSpliceExpr, { + TRY_TO(TraverseStmt(S->getOperand())); +}) +DEF_TRAVERSE_STMT(CXXExprSpliceExpr, { + TRY_TO(TraverseStmt(const_cast(S->getOperand()))); +}) +DEF_TRAVERSE_STMT(CXXDependentMemberSpliceExpr, { + TRY_TO(TraverseStmt(S->getBase())); + TRY_TO(TraverseStmt(S->getRHS())); +}) +DEF_TRAVERSE_STMT(StackLocationExpr, {}) +DEF_TRAVERSE_STMT(ValueOfLValueExpr, { + TRY_TO(TraverseDecl(S->getValueDecl())); +}) DEF_TRAVERSE_STMT(CXXParenListInitExpr, {}) DEF_TRAVERSE_STMT(MaterializeTemporaryExpr, { diff --git a/clang/include/clang/AST/Reflection.h b/clang/include/clang/AST/Reflection.h new file mode 100644 index 0000000000000..79f120e64fefe --- /dev/null +++ b/clang/include/clang/AST/Reflection.h @@ -0,0 +1,162 @@ +//===--- Reflection.h - Classes for representing reflection -----*- C++ -*-===// +// +// Copyright 2024 Bloomberg Finance L.P. +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +/// +/// \file +/// \brief Defines facilities for representing reflected entities. +/// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_AST_REFLECTION_H +#define LLVM_CLANG_AST_REFLECTION_H + +#include "clang/AST/Type.h" +#include "llvm/ADT/FoldingSet.h" +#include +#include + +namespace clang { + +class APValue; +class ASTContext; +class CXXBaseSpecifier; +class ConstantExpr; +class NamespaceDecl; +class ValueDecl; + +struct TagDataMemberSpec; + +/// \brief Representation of a reflection value holding an opaque pointer to one +/// or more entities. +class ReflectionValue { +public: + /// \brief The kind of construct reflected. + enum ReflectionKind { + /// \brief A reflection of a type. Corresponds to an object of type + /// QualType. + RK_type = 1, + + /// \brief A reflection of an expression that constant evaluates to a + /// prvalue. Corresponds to an object of type ConstantExpr. + RK_const_value = 2, + + /// \brief A reflection of an expression referencing an entity (e.g., + /// variable, function, member function, etc). Corresponds to an object of + /// type Decl. + RK_declaration = 3, + + /// \brief A reflection of a template (e.g., class template, variable + /// template, function template, alias template, concept). + RK_template = 4, + + /// \brief A reflection of a namespace. Corresponds to an object of type + /// Decl. + /// + /// A namespace could be represented as a TranslationUnitDecl (for the + /// global namespace), a NamespaceAliasDecl (for namespace aliases), or a + /// NamespaceDecl (for all other namespaces). Somewhat annoyingly, these + /// classes have no nearer common ancestor than the base Decl class. + RK_namespace = 5, + + /// \brief A reflection of a base class specifier. Corresponds to an object + /// of type CXXBaseSpecifier. + RK_base_specifier = 6, + + /// \brief A reflection of a description of a hypothetical data member + /// (static or nonstatic) that might belong to a class or union. Corresponds + /// to an object of type TagDataMemberSpec. + /// + /// This is specifically used for 'std::meta::data_member_description' and + /// 'std::meta::define_class'. If the surface area of 'define_class' grows + /// (i.e., supports additional types of "descriptions", e.g., for member + /// functions), it would be nice to find a more generic way to do this. One + /// idea is to allow a reflection of a type erased struct, but this seemed + /// like a tolerable idea for the time being. + RK_data_member_spec = 7, + }; + +private: + ReflectionKind Kind; + void *Entity; + +public: + ReflectionValue() = default; + ReflectionValue(ReflectionValue const&Rhs); + ReflectionValue(ReflectionKind ReflKind, void *Entity); + ReflectionValue &operator=(ReflectionValue const& Rhs); + + ReflectionKind getKind() const { + return Kind; + } + void *getOpaqueValue() const { + return Entity; + } + + /// Returns this as a type operand. + QualType getAsType() const; + + /// Returns this as an expression. + ConstantExpr *getAsConstValueExpr() const { + assert(getKind() == RK_const_value && "not a constant value"); + return reinterpret_cast(Entity); + } + + /// Returns this as a declaration that can hold a value. + ValueDecl *getAsDecl() const { + assert(getKind() == RK_declaration && "not a declaration reference"); + return reinterpret_cast(Entity); + } + + /// Returns this as a template name. + TemplateName getAsTemplate() const { + assert(getKind() == RK_template && "not a template"); + return TemplateName::getFromVoidPointer(const_cast(Entity)); + } + + /// Returns this as a namespace declaration. + Decl *getAsNamespace() const { + assert(getKind() == RK_namespace && "not a namespace"); + return reinterpret_cast(Entity); + } + + CXXBaseSpecifier *getAsBaseSpecifier() const { + assert(getKind() == RK_base_specifier && "not a base class specifier"); + return reinterpret_cast(Entity); + } + + /// Returns this as a struct describing a hypothetical data member. + TagDataMemberSpec *getAsDataMemberSpec() const { + assert(getKind() == RK_data_member_spec && "not a data member spec"); + return reinterpret_cast(Entity); + } + + void Profile(llvm::FoldingSetNodeID &ID) const; + + bool operator==(ReflectionValue const& Rhs) const; + bool operator!=(ReflectionValue const& Rhs) const; +}; + +/// \brief Representation of a hypothetical data member, which could be used to +/// complete an incomplete class definition using the 'std::meta::define_class' +/// standard library function. +struct TagDataMemberSpec { + QualType Ty; + + std::optional Name; + bool IsStatic; + std::optional Alignment; + std::optional BitWidth; + + bool operator==(TagDataMemberSpec const& Rhs) const; + bool operator!=(TagDataMemberSpec const& Rhs) const; +}; + +} // namespace clang + +#endif diff --git a/clang/include/clang/AST/TemplateArgumentVisitor.h b/clang/include/clang/AST/TemplateArgumentVisitor.h index cf0d322015806..800e8c7562efd 100644 --- a/clang/include/clang/AST/TemplateArgumentVisitor.h +++ b/clang/include/clang/AST/TemplateArgumentVisitor.h @@ -37,6 +37,8 @@ class Base { DISPATCH(Declaration); DISPATCH(NullPtr); DISPATCH(Integral); + DISPATCH(Reflection); + DISPATCH(IndeterminateSplice); DISPATCH(StructuralValue); DISPATCH(Template); DISPATCH(TemplateExpansion); @@ -60,6 +62,8 @@ class Base { VISIT_METHOD(Declaration); VISIT_METHOD(NullPtr); VISIT_METHOD(Integral); + VISIT_METHOD(Reflection); + VISIT_METHOD(IndeterminateSplice); VISIT_METHOD(StructuralValue); VISIT_METHOD(Template); VISIT_METHOD(TemplateExpansion); diff --git a/clang/include/clang/AST/TemplateBase.h b/clang/include/clang/AST/TemplateBase.h index fea2c8ccfee67..e76667ccc94da 100644 --- a/clang/include/clang/AST/TemplateBase.h +++ b/clang/include/clang/AST/TemplateBase.h @@ -1,5 +1,7 @@ //===- TemplateBase.h - Core classes for C++ templates ----------*- C++ -*-===// // +// Copyright 2024 Bloomberg Finance L.P. +// // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception @@ -16,6 +18,7 @@ #include "clang/AST/DependenceFlags.h" #include "clang/AST/NestedNameSpecifier.h" +#include "clang/AST/Reflection.h" #include "clang/AST/TemplateName.h" #include "clang/AST/Type.h" #include "clang/Basic/LLVM.h" @@ -24,6 +27,7 @@ #include "llvm/ADT/APSInt.h" #include "llvm/ADT/ArrayRef.h" #include "llvm/ADT/SmallVector.h" +#include "llvm/Support/AlignOf.h" #include "llvm/Support/Compiler.h" #include "llvm/Support/TrailingObjects.h" #include @@ -52,6 +56,7 @@ namespace clang { class APValue; class ASTContext; +class CXXIndeterminateSpliceExpr; class Expr; struct PrintingPolicy; class TypeSourceInfo; @@ -81,6 +86,14 @@ class TemplateArgument { /// that was provided for an integral non-type template parameter. Integral, + /// The template argument is a reflection value that was provided for a + /// meta::info non-type template parameter. + Reflection, + + /// The template argument is an indeterminate splice of a reflection, which + /// might reflect any of: a type, an expression, or a class template. + IndeterminateSplice, + /// The template argument is a non-type template argument that can't be /// represented by the special-case Declaration, NullPtr, or Integral /// forms. These values are only ever produced by constant evaluation, @@ -138,6 +151,14 @@ class TemplateArgument { }; void *Type; }; + struct R { + LLVM_PREFERRED_TYPE(ArgKind) + unsigned Kind : 31; + LLVM_PREFERRED_TYPE(bool) + unsigned IsDefaulted : 1; + llvm::AlignedCharArrayUnion Value; + void *Type; + }; struct V { LLVM_PREFERRED_TYPE(ArgKind) unsigned Kind : 31; @@ -172,6 +193,7 @@ class TemplateArgument { union { struct DA DeclArg; struct I Integer; + struct R ReflectionArg; struct V Value; struct A Args; struct TA TemplateArg; @@ -210,6 +232,15 @@ class TemplateArgument { TemplateArgument(const ASTContext &Ctx, QualType Type, const APValue &Value, bool IsDefaulted = false); + /// Construct a reflection template argument. The memory to store the value + /// is allocated with Ctx. + TemplateArgument(ASTContext &Ctx, const ReflectionValue &Value, + bool IsDefaulted = false); + + /// Construct an indeterminate splice template argument. + TemplateArgument(CXXIndeterminateSpliceExpr *Splice, + bool IsDefaulted = false); + /// Construct an integral constant template argument with the same /// value as Other but a different type. TemplateArgument(const TemplateArgument &Other, QualType Type) { @@ -373,12 +404,31 @@ class TemplateArgument { Integer.IsUnsigned); } + /// Retrieve the template argument as an integral value. + const ReflectionValue& getAsReflection() const { + assert(getKind() == Reflection && "Unexpected kind"); + + return *reinterpret_cast( + (const char *)&ReflectionArg.Value); + } + + CXXIndeterminateSpliceExpr *getAsIndeterminateSplice() const { + assert(getKind() == IndeterminateSplice && "Unexpected kind"); + return reinterpret_cast(TypeOrValue.V); + } + /// Retrieve the type of the integral value. QualType getIntegralType() const { assert(getKind() == Integral && "Unexpected kind"); return QualType::getFromOpaquePtr(Integer.Type); } + /// Retrieve the type of the reflection value. + QualType getReflectionType() const { + assert(getKind() == Reflection && "Unexpected kind"); + return QualType::getFromOpaquePtr(ReflectionArg.Type); + } + void setIntegralType(QualType T) { assert(getKind() == Integral && "Unexpected kind"); Integer.Type = T.getAsOpaquePtr(); @@ -544,6 +594,8 @@ class TemplateArgumentLoc { // expression. assert(Argument.getKind() == TemplateArgument::NullPtr || Argument.getKind() == TemplateArgument::Integral || + Argument.getKind() == TemplateArgument::Reflection || + Argument.getKind() == TemplateArgument::IndeterminateSplice || Argument.getKind() == TemplateArgument::Declaration || Argument.getKind() == TemplateArgument::StructuralValue || Argument.getKind() == TemplateArgument::Expression); @@ -601,6 +653,16 @@ class TemplateArgumentLoc { return LocInfo.getAsExpr(); } + Expr *getSourceReflectionExpression() const { + assert(Argument.getKind() == TemplateArgument::Reflection); + return LocInfo.getAsExpr(); + } + + Expr *getSourceIndeterminateSpliceExpression() const { + assert(Argument.getKind() == TemplateArgument::IndeterminateSplice); + return LocInfo.getAsExpr(); + } + Expr *getSourceStructuralValueExpression() const { assert(Argument.getKind() == TemplateArgument::StructuralValue); return LocInfo.getAsExpr(); diff --git a/clang/include/clang/AST/TemplateName.h b/clang/include/clang/AST/TemplateName.h index b7732e54ba107..47aafe069d67c 100644 --- a/clang/include/clang/AST/TemplateName.h +++ b/clang/include/clang/AST/TemplateName.h @@ -26,6 +26,7 @@ namespace clang { class ASTContext; +class CXXIndeterminateSpliceExpr; class Decl; class DependentTemplateName; class IdentifierInfo; @@ -351,7 +352,7 @@ class TemplateName { /// error. void dump() const; - void Profile(llvm::FoldingSetNodeID &ID); + void Profile(llvm::FoldingSetNodeID &ID) const; /// Retrieve the template name as a void pointer. void *getAsVoidPointer() const { return Storage.getOpaqueValue(); } @@ -488,13 +489,19 @@ class QualifiedTemplateName : public llvm::FoldingSetNode { class DependentTemplateName : public llvm::FoldingSetNode { friend class ASTContext; + enum Kind { + DTNK_Identifier, + DTNK_Operator, + DTNK_IndeterminateSplice, + }; + /// The nested name specifier that qualifies the template /// name. /// /// The bit stored in this qualifier describes whether the \c Name field /// is interpreted as an IdentifierInfo pointer (when clear) or as an /// overloaded operator kind (when set). - llvm::PointerIntPair Qualifier; + llvm::PointerIntPair Qualifier; /// The dependent template name. union { @@ -507,6 +514,11 @@ class DependentTemplateName : public llvm::FoldingSetNode { /// /// Only valid when the bit on \c Qualifier is set. OverloadedOperatorKind Operator; + + /// The dependent splice expression. + /// + /// Only valid when the NestedNameSpecifier on \c Qualifier is unset. + CXXIndeterminateSpliceExpr *SpliceExpr; }; /// The canonical template name to which this dependent @@ -519,32 +531,36 @@ class DependentTemplateName : public llvm::FoldingSetNode { DependentTemplateName(NestedNameSpecifier *Qualifier, const IdentifierInfo *Identifier) - : Qualifier(Qualifier, false), Identifier(Identifier), + : Qualifier(Qualifier, DTNK_Identifier), Identifier(Identifier), CanonicalTemplateName(this) {} DependentTemplateName(NestedNameSpecifier *Qualifier, const IdentifierInfo *Identifier, TemplateName Canon) - : Qualifier(Qualifier, false), Identifier(Identifier), + : Qualifier(Qualifier, DTNK_Identifier), Identifier(Identifier), CanonicalTemplateName(Canon) {} DependentTemplateName(NestedNameSpecifier *Qualifier, OverloadedOperatorKind Operator) - : Qualifier(Qualifier, true), Operator(Operator), + : Qualifier(Qualifier, DTNK_Operator), Operator(Operator), CanonicalTemplateName(this) {} DependentTemplateName(NestedNameSpecifier *Qualifier, OverloadedOperatorKind Operator, TemplateName Canon) - : Qualifier(Qualifier, true), Operator(Operator), + : Qualifier(Qualifier, DTNK_Operator), Operator(Operator), CanonicalTemplateName(Canon) {} + DependentTemplateName(CXXIndeterminateSpliceExpr *SpliceExpr) + : Qualifier(nullptr, DTNK_IndeterminateSplice), SpliceExpr(SpliceExpr), + CanonicalTemplateName(this) {} + public: /// Return the nested name specifier that qualifies this name. NestedNameSpecifier *getQualifier() const { return Qualifier.getPointer(); } /// Determine whether this template name refers to an identifier. - bool isIdentifier() const { return !Qualifier.getInt(); } + bool isIdentifier() const { return Qualifier.getInt() == DTNK_Identifier; } /// Returns the identifier to which this template name refers. const IdentifierInfo *getIdentifier() const { @@ -554,7 +570,9 @@ class DependentTemplateName : public llvm::FoldingSetNode { /// Determine whether this template name refers to an overloaded /// operator. - bool isOverloadedOperator() const { return Qualifier.getInt(); } + bool isOverloadedOperator() const { + return Qualifier.getInt() == DTNK_Operator; + } /// Return the overloaded operator to which this template name refers. OverloadedOperatorKind getOperator() const { @@ -563,26 +581,46 @@ class DependentTemplateName : public llvm::FoldingSetNode { return Operator; } + /// Determine whether this template name refers to an indeterminate splice. + bool isIndeterminateSplice() const { + return Qualifier.getInt() == DTNK_IndeterminateSplice; + } + + CXXIndeterminateSpliceExpr *getIndeterminateSplice() const { + assert(isIndeterminateSplice() && + "Template name isn't an indeterminate splice?"); + return SpliceExpr; + } + void Profile(llvm::FoldingSetNodeID &ID) { if (isIdentifier()) Profile(ID, getQualifier(), getIdentifier()); - else + else if (isOverloadedOperator()) Profile(ID, getQualifier(), getOperator()); + else + Profile(ID, getIndeterminateSplice()); } static void Profile(llvm::FoldingSetNodeID &ID, NestedNameSpecifier *NNS, const IdentifierInfo *Identifier) { ID.AddPointer(NNS); - ID.AddBoolean(false); + ID.AddInteger(DTNK_Identifier); ID.AddPointer(Identifier); } static void Profile(llvm::FoldingSetNodeID &ID, NestedNameSpecifier *NNS, OverloadedOperatorKind Operator) { ID.AddPointer(NNS); - ID.AddBoolean(true); + ID.AddInteger(DTNK_Operator); ID.AddInteger(Operator); } + + static void Profile(llvm::FoldingSetNodeID &ID, + CXXIndeterminateSpliceExpr *Splice) { + ID.AddPointer(nullptr); + ID.AddPointer(Splice); + ID.AddInteger(DTNK_IndeterminateSplice); + } }; } // namespace clang. diff --git a/clang/include/clang/AST/Type.h b/clang/include/clang/AST/Type.h index 99f45d518c796..fe5b430f0fbd5 100644 --- a/clang/include/clang/AST/Type.h +++ b/clang/include/clang/AST/Type.h @@ -1,5 +1,7 @@ //===- Type.h - C Language Family Type Representation -----------*- C++ -*-===// // +// Copyright 2024 Bloomberg Finance L.P. +// // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception @@ -1623,6 +1625,9 @@ class alignas(TypeAlignment) Type : public ExtQualsTypeCommonBase { LLVM_PREFERRED_TYPE(TypeClass) unsigned TC : 8; + /// Whether this type is allowd only in constexpr contexts. + unsigned MetaType : 1; + /// Store information on the type dependency. LLVM_PREFERRED_TYPE(TypeDependence) unsigned Dependence : llvm::BitWidth; @@ -1658,7 +1663,7 @@ class alignas(TypeAlignment) Type : public ExtQualsTypeCommonBase { return CachedLocalOrUnnamed; } }; - enum { NumTypeBits = 8 + llvm::BitWidth + 6 }; + enum { NumTypeBits = 9 + llvm::BitWidth + 6 }; protected: // These classes allow subclasses to somewhat cleanly pack bitfields @@ -2055,7 +2060,7 @@ class alignas(TypeAlignment) Type : public ExtQualsTypeCommonBase { protected: friend class ASTContext; - Type(TypeClass tc, QualType canon, TypeDependence Dependence) + Type(TypeClass tc, QualType canon, TypeDependence Dependence, bool MetaType) : ExtQualsTypeCommonBase(this, canon.isNull() ? QualType(this_(), 0) : canon) { static_assert(sizeof(*this) <= @@ -2064,6 +2069,7 @@ class alignas(TypeAlignment) Type : public ExtQualsTypeCommonBase { static_assert(alignof(decltype(*this)) % TypeAlignment == 0, "Insufficient alignment!"); TypeBits.TC = tc; + TypeBits.MetaType = MetaType; TypeBits.Dependence = static_cast(Dependence); TypeBits.CacheValid = false; TypeBits.CachedLocalOrUnnamed = false; @@ -2080,6 +2086,10 @@ class alignas(TypeAlignment) Type : public ExtQualsTypeCommonBase { void addDependence(TypeDependence D) { setDependence(getDependence() | D); } + void setMetaType(bool M = true) { + TypeBits.MetaType = M; + } + public: friend class ASTReader; friend class ASTWriter; @@ -2272,6 +2282,7 @@ class alignas(TypeAlignment) Type : public ExtQualsTypeCommonBase { bool isRealType() const; // C99 6.2.5p17 (real floating + integer) bool isArithmeticType() const; // C99 6.2.5p18 (integer + floating) bool isVoidType() const; // C99 6.2.5p19 + bool isReflectionType() const; // C++2c reflection [P2996] bool isScalarType() const; // C99 6.2.5p21 (arithmetic + pointers) bool isAggregateType() const; bool isFundamentalType() const; @@ -2305,6 +2316,7 @@ class alignas(TypeAlignment) Type : public ExtQualsTypeCommonBase { bool isRecordType() const; bool isClassType() const; bool isStructureType() const; + bool isMetaType() const; bool isObjCBoxableRecordType() const; bool isInterfaceType() const; bool isStructureOrClassType() const; @@ -2801,7 +2813,8 @@ class BuiltinType : public Type { BuiltinType(Kind K) : Type(Builtin, QualType(), K == Dependent ? TypeDependence::DependentInstantiation - : TypeDependence::None) { + : TypeDependence::None, + /*MetaType=*/(K == MetaInfo)) { static_assert(Kind::LastKind < (1 << BuiltinTypeBitfields::NumOfBuiltinTypeBits) && "Defined builtin type exceeds the allocated space for serial " @@ -2879,7 +2892,8 @@ class ComplexType : public Type, public llvm::FoldingSetNode { QualType ElementType; ComplexType(QualType Element, QualType CanonicalPtr) - : Type(Complex, CanonicalPtr, Element->getDependence()), + : Type(Complex, CanonicalPtr, Element->getDependence(), + /*MetaType=*/false), ElementType(Element) {} public: @@ -2906,7 +2920,8 @@ class ParenType : public Type, public llvm::FoldingSetNode { QualType Inner; ParenType(QualType InnerType, QualType CanonType) - : Type(Paren, CanonType, InnerType->getDependence()), Inner(InnerType) {} + : Type(Paren, CanonType, InnerType->getDependence(), /*MetaType=*/false), + Inner(InnerType) {} public: QualType getInnerType() const { return Inner; } @@ -2932,7 +2947,8 @@ class PointerType : public Type, public llvm::FoldingSetNode { QualType PointeeType; PointerType(QualType Pointee, QualType CanonicalPtr) - : Type(Pointer, CanonicalPtr, Pointee->getDependence()), + : Type(Pointer, CanonicalPtr, Pointee->getDependence(), + Pointee->isMetaType()), PointeeType(Pointee) {} public: @@ -3094,7 +3110,7 @@ class AdjustedType : public Type, public llvm::FoldingSetNode { AdjustedType(TypeClass TC, QualType OriginalTy, QualType AdjustedTy, QualType CanonicalPtr) - : Type(TC, CanonicalPtr, OriginalTy->getDependence()), + : Type(TC, CanonicalPtr, OriginalTy->getDependence(), /*MetaType=*/false), OriginalTy(OriginalTy), AdjustedTy(AdjustedTy) {} public: @@ -3143,7 +3159,8 @@ class BlockPointerType : public Type, public llvm::FoldingSetNode { QualType PointeeType; BlockPointerType(QualType Pointee, QualType CanonicalCls) - : Type(BlockPointer, CanonicalCls, Pointee->getDependence()), + : Type(BlockPointer, CanonicalCls, Pointee->getDependence(), + /*MetaType=*/false), PointeeType(Pointee) {} public: @@ -3173,7 +3190,8 @@ class ReferenceType : public Type, public llvm::FoldingSetNode { protected: ReferenceType(TypeClass tc, QualType Referencee, QualType CanonicalRef, bool SpelledAsLValue) - : Type(tc, CanonicalRef, Referencee->getDependence()), + : Type(tc, CanonicalRef, Referencee->getDependence(), + Referencee->isMetaType()), PointeeType(Referencee) { ReferenceTypeBits.SpelledAsLValue = SpelledAsLValue; ReferenceTypeBits.InnerRef = Referencee->isReferenceType(); @@ -3259,7 +3277,7 @@ class MemberPointerType : public Type, public llvm::FoldingSetNode { MemberPointerType(QualType Pointee, const Type *Cls, QualType CanonicalPtr) : Type(MemberPointer, CanonicalPtr, (Cls->getDependence() & ~TypeDependence::VariablyModified) | - Pointee->getDependence()), + Pointee->getDependence(), /*MetaType=*/false), PointeeType(Pointee), Class(Cls) {} public: @@ -4348,7 +4366,7 @@ class FunctionType : public Type { protected: FunctionType(TypeClass tc, QualType res, QualType Canonical, TypeDependence Dependence, ExtInfo Info) - : Type(tc, Canonical, Dependence), ResultType(res) { + : Type(tc, Canonical, Dependence, /*MetaType=*/false), ResultType(res) { FunctionTypeBits.ExtInfo = Info.Bits; } @@ -4938,7 +4956,7 @@ class UnresolvedUsingType : public Type { UnresolvedUsingType(const UnresolvedUsingTypenameDecl *D) : Type(UnresolvedUsing, QualType(), - TypeDependence::DependentInstantiation), + TypeDependence::DependentInstantiation, /*MetaType=*/false), Decl(const_cast(D)) {} public: @@ -5037,7 +5055,8 @@ class MacroQualifiedType : public Type { MacroQualifiedType(QualType UnderlyingTy, QualType CanonTy, const IdentifierInfo *MacroII) - : Type(MacroQualified, CanonTy, UnderlyingTy->getDependence()), + : Type(MacroQualified, CanonTy, UnderlyingTy->getDependence(), + UnderlyingTy->isMetaType()), UnderlyingTy(UnderlyingTy), MacroII(MacroII) { assert(isa(UnderlyingTy) && "Expected a macro qualified type to only wrap attributed types."); @@ -5118,7 +5137,7 @@ class TypeOfType : public Type { : Type(TypeOf, Kind == TypeOfKind::Unqualified ? Can.getAtomicUnqualifiedType() : Can, - T->getDependence()), + T->getDependence(), /*MetaType=*/false), TOType(T) { TypeOfBits.IsUnqual = Kind == TypeOfKind::Unqualified; } @@ -5403,7 +5422,8 @@ class AttributedType : public Type, public llvm::FoldingSetNode { AttributedType(QualType canon, attr::Kind attrKind, QualType modified, QualType equivalent) - : Type(Attributed, canon, equivalent->getDependence()), + : Type(Attributed, canon, equivalent->getDependence(), + /*MetaType=*/false), ModifiedType(modified), EquivalentType(equivalent) { AttributedTypeBits.AttrKind = attrKind; } @@ -5499,7 +5519,8 @@ class BTFTagAttributedType : public Type, public llvm::FoldingSetNode { BTFTagAttributedType(QualType Canon, QualType Wrapped, const BTFTypeTagAttr *BTFAttr) - : Type(BTFTagAttributed, Canon, Wrapped->getDependence()), + : Type(BTFTagAttributed, Canon, Wrapped->getDependence(), + /*MetaType=*/false), WrappedType(Wrapped), BTFAttr(BTFAttr) {} public: @@ -5546,14 +5567,16 @@ class TemplateTypeParmType : public Type, public llvm::FoldingSetNode { TemplateTypeParmType(TemplateTypeParmDecl *TTPDecl, QualType Canon) : Type(TemplateTypeParm, Canon, TypeDependence::DependentInstantiation | - (Canon->getDependence() & TypeDependence::UnexpandedPack)), + (Canon->getDependence() & TypeDependence::UnexpandedPack), + /*MetaType=*/false), TTPDecl(TTPDecl) {} /// Build the canonical type. TemplateTypeParmType(unsigned D, unsigned I, bool PP) : Type(TemplateTypeParm, QualType(this, 0), TypeDependence::DependentInstantiation | - (PP ? TypeDependence::UnexpandedPack : TypeDependence::None)) { + (PP ? TypeDependence::UnexpandedPack : TypeDependence::None), + /*MetaType=*/false) { CanTTPTInfo.Depth = D; CanTTPTInfo.Index = I; CanTTPTInfo.ParameterPack = PP; @@ -5744,7 +5767,8 @@ class DeducedType : public Type { ExtraDependence | (DeducedAsType.isNull() ? TypeDependence::None : DeducedAsType->getDependence() & - ~TypeDependence::VariablyModified)), + ~TypeDependence::VariablyModified), + /*MetaType=*/false), DeducedAsType(DeducedAsType) {} public: @@ -6031,7 +6055,7 @@ class InjectedClassNameType : public Type { InjectedClassNameType(CXXRecordDecl *D, QualType TST) : Type(InjectedClassName, QualType(), - TypeDependence::DependentInstantiation), + TypeDependence::DependentInstantiation, /*MetaType=*/false), Decl(D), InjectedType(TST) { assert(isa(TST)); assert(!TST.hasQualifiers()); @@ -6111,7 +6135,7 @@ class TypeWithKeyword : public Type { protected: TypeWithKeyword(ElaboratedTypeKeyword Keyword, TypeClass tc, QualType Canonical, TypeDependence Dependence) - : Type(tc, Canonical, Dependence) { + : Type(tc, Canonical, Dependence, /*MetaType=*/false) { TypeWithKeywordBits.Keyword = llvm::to_underlying(Keyword); } @@ -6367,7 +6391,7 @@ class PackExpansionType : public Type, public llvm::FoldingSetNode { : Type(PackExpansion, Canon, (Pattern->getDependence() | TypeDependence::Dependent | TypeDependence::Instantiation) & - ~TypeDependence::UnexpandedPack), + ~TypeDependence::UnexpandedPack, /*MetaType=*/false), Pattern(Pattern) { PackExpansionTypeBits.NumExpansions = NumExpansions ? *NumExpansions + 1 : 0; @@ -6407,6 +6431,59 @@ class PackExpansionType : public Type, public llvm::FoldingSetNode { } }; +/// Represents a type formed by evaluating a reflection splice (C++2c, P2996). +/// +/// A reflection splice wraps a potentially dependent constant expression whose +/// resulting APValue holds a ReflectionValue; this is expected to hold a type +/// in the context of a 'ReflectionSpliceType'. +class ReflectionSpliceType : public Type { + Expr *Operand; + QualType UnderlyingTy; + +protected: + friend class ASTContext; + + ReflectionSpliceType(Expr *Operand, QualType T, QualType Canon = QualType()); + +public: + /// Returns the operand of the splice. + Expr *getOperand() const { return Operand; } + + /// Returns the underlying type (i.e., the one spliced). + QualType getUnderlyingType() const { return UnderlyingTy; } + + /// Removes a single level of sugar. + QualType desugar() const; + + /// Returns whether this type directly provides sugar. + bool isSugared() const; + + static bool classof(const Type *T) { + return T->getTypeClass() == ReflectionSplice; + } +}; + +/// Represents a dependent type formed by evaluating a reflection splice +/// (C++2c, P2996). +/// +/// For these types, we won't actually know what the type is until the +/// splice operand is evaluated, at which point this will become a +/// ReflectionSpliceType. +class DependentReflectionSpliceType : public ReflectionSpliceType, + public llvm::FoldingSetNode { + const ASTContext &Context; + +public: + DependentReflectionSpliceType(const ASTContext &Context, Expr *E); + + void Profile(llvm::FoldingSetNodeID &ID) { + Profile(ID, Context, getOperand()); + } + + static void Profile(llvm::FoldingSetNodeID &ID, const ASTContext &Context, + Expr *Operand); +}; + /// This class wraps the list of protocol qualifiers. For types that can /// take ObjC protocol qualifers, they can subclass this class. template @@ -6587,7 +6664,8 @@ class ObjCObjectType : public Type, bool isKindOf); ObjCObjectType(enum Nonce_ObjCInterface) - : Type(ObjCInterface, QualType(), TypeDependence::None), + : Type(ObjCInterface, QualType(), TypeDependence::None, + /*MetaType=*/false), BaseType(QualType(this_(), 0)) { ObjCObjectTypeBits.NumProtocols = 0; ObjCObjectTypeBits.NumTypeArgs = 0; @@ -6801,7 +6879,8 @@ class ObjCObjectPointerType : public Type, public llvm::FoldingSetNode { QualType PointeeType; ObjCObjectPointerType(QualType Canonical, QualType Pointee) - : Type(ObjCObjectPointer, Canonical, Pointee->getDependence()), + : Type(ObjCObjectPointer, Canonical, Pointee->getDependence(), + /*MetaType=*/false), PointeeType(Pointee) {} public: @@ -6971,7 +7050,8 @@ class AtomicType : public Type, public llvm::FoldingSetNode { QualType ValueType; AtomicType(QualType ValTy, QualType Canonical) - : Type(Atomic, Canonical, ValTy->getDependence()), ValueType(ValTy) {} + : Type(Atomic, Canonical, ValTy->getDependence(), /*MetaType=*/false), + ValueType(ValTy) {} public: /// Gets the type contained by this atomic type, i.e. @@ -7002,7 +7082,7 @@ class PipeType : public Type, public llvm::FoldingSetNode { bool isRead; PipeType(QualType elemType, QualType CanonicalPtr, bool isRead) - : Type(Pipe, CanonicalPtr, elemType->getDependence()), + : Type(Pipe, CanonicalPtr, elemType->getDependence(), /*MetaType=*/false), ElementType(elemType), isRead(isRead) {} public: @@ -7696,6 +7776,13 @@ inline bool Type::isVoidType() const { return isSpecificBuiltinType(BuiltinType::Void); } +inline bool Type::isReflectionType() const { + if (const auto *BT = dyn_cast(CanonicalType)) { + return BT->getKind() == BuiltinType::MetaInfo; + } + return false; +} + inline bool Type::isHalfType() const { // FIXME: Should we allow complex __fp16? Probably not. return isSpecificBuiltinType(BuiltinType::Half); @@ -7794,7 +7881,7 @@ inline bool Type::isUnsignedFixedPointType() const { inline bool Type::isScalarType() const { if (const auto *BT = dyn_cast(CanonicalType)) return BT->getKind() > BuiltinType::Void && - BT->getKind() <= BuiltinType::NullPtr; + BT->getKind() <= BuiltinType::MetaInfo; if (const EnumType *ET = dyn_cast(CanonicalType)) // Enums are scalar types, but only if they are defined. Incomplete enums // are not treated as scalar types. diff --git a/clang/include/clang/AST/TypeLoc.h b/clang/include/clang/AST/TypeLoc.h index 9f2dff7a782cb..431686ac7b239 100644 --- a/clang/include/clang/AST/TypeLoc.h +++ b/clang/include/clang/AST/TypeLoc.h @@ -1,5 +1,7 @@ //===- TypeLoc.h - Type Source Info Wrapper ---------------------*- C++ -*-===// // +// Copyright 2024 Bloomberg Finance L.P. +// // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception @@ -2600,6 +2602,41 @@ class PackExpansionTypeLoc } }; +struct ReflectionSpliceTypeLocInfo { + SourceLocation LSpliceLoc, RSpliceLoc; +}; + +class ReflectionSpliceTypeLoc + : public ConcreteTypeLoc { +public: + Expr *getOperand() const { + return getTypePtr()->getOperand(); + } + + SourceLocation getLSpliceLoc() const { + return this->getLocalData()->LSpliceLoc; + } + + void setLSpliceLoc(SourceLocation Loc) { + this->getLocalData()->LSpliceLoc = Loc; + } + + SourceLocation getRSpliceLoc() const { + return this->getLocalData()->RSpliceLoc; + } + + void setRSpliceLoc(SourceLocation Loc) { + this->getLocalData()->RSpliceLoc = Loc; + } + + void initializeLocal(ASTContext &Context, SourceLocation Loc); + + SourceRange getLocalSourceRange() const { + return SourceRange(getLSpliceLoc(), getRSpliceLoc()); + } +}; + struct AtomicTypeLocInfo { SourceLocation KWLoc, LParenLoc, RParenLoc; }; diff --git a/clang/include/clang/AST/TypeProperties.td b/clang/include/clang/AST/TypeProperties.td index 40dd16f080e2e..3e5dbfb35c61f 100644 --- a/clang/include/clang/AST/TypeProperties.td +++ b/clang/include/clang/AST/TypeProperties.td @@ -1,5 +1,7 @@ //==--- TypeProperties.td - Type property definitions ---------------------===// // +// Copyright 2024 Bloomberg Finance L.P. +// // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception @@ -810,6 +812,19 @@ let Class = PackExpansionType in { }]>; } +let Class = ReflectionSpliceType in { + def : Property<"underlyingType", QualType> { + let Read = [{ node->getUnderlyingType() }]; + } + def : Property<"operand", ExprRef> { + let Read = [{ node->getOperand() }]; + } + + def : Creator<[{ + return ctx.getReflectionSpliceType(operand, underlyingType); + }]>; +} + let Class = SubstTemplateTypeParmPackType in { def : Property<"associatedDecl", DeclRef> { let Read = [{ node->getAssociatedDecl() }]; diff --git a/clang/include/clang/Basic/DeclNodes.td b/clang/include/clang/Basic/DeclNodes.td index 48396e85c5ada..94b36988afa0e 100644 --- a/clang/include/clang/Basic/DeclNodes.td +++ b/clang/include/clang/Basic/DeclNodes.td @@ -16,6 +16,7 @@ def PragmaDetectMismatch : DeclNode; def ExternCContext : DeclNode, DeclContext; def Named : DeclNode; def Namespace : DeclNode, DeclContext; + def DependentNamespace : DeclNode; def UsingDirective : DeclNode; def NamespaceAlias : DeclNode; def Label : DeclNode; diff --git a/clang/include/clang/Basic/DiagnosticFrontendKinds.td b/clang/include/clang/Basic/DiagnosticFrontendKinds.td index 14b08d4927ec5..cfd700dc8c59a 100644 --- a/clang/include/clang/Basic/DiagnosticFrontendKinds.td +++ b/clang/include/clang/Basic/DiagnosticFrontendKinds.td @@ -99,6 +99,11 @@ def warn_toc_unsupported_type : Warning<"-mtocdata option is ignored " def err_fe_invalid_code_complete_file : Error< "cannot locate code-completion file %0">, DefaultFatal; +def err_fe_reflection_incompatible_with_blocks : Error< + "cannot specify both '-freflection' and '-fblocks'">, DefaultFatal; +def err_fe_parameter_reflection_without_reflection : Error< + "cannot specify '-fparameter-reflection' without '-freflection'">, + DefaultFatal; def err_fe_dependency_file_requires_MT : Error< "-dependency-file requires at least one -MT or -MQ option">; def err_fe_invalid_plugin_name : Error< @@ -322,6 +327,9 @@ def err_non_default_visibility_dllimport : Error< def err_ifunc_resolver_return : Error< "ifunc resolver function must return a pointer">; +def err_runtime_meta_info : Error< + "cannot emit a runtime definition of reflection type">; + def warn_atomic_op_misaligned : Warning< "misaligned atomic operation may incur " "significant performance penalty" diff --git a/clang/include/clang/Basic/DiagnosticLexKinds.td b/clang/include/clang/Basic/DiagnosticLexKinds.td index ad6bacfb118d4..99d13c04ba684 100644 --- a/clang/include/clang/Basic/DiagnosticLexKinds.td +++ b/clang/include/clang/Basic/DiagnosticLexKinds.td @@ -944,6 +944,10 @@ def warn_pp_include_angled_in_module_purview : Warning< "the module declaration">, InGroup>; +def warn_reflection_disabled : Warning< + "not parsing token '%0'; use '-freflection' to enable reflection features">, + InGroup>; + def warn_header_guard : Warning< "%0 is used as a header guard here, followed by #define of a different macro">, InGroup>; diff --git a/clang/include/clang/Basic/DiagnosticParseKinds.td b/clang/include/clang/Basic/DiagnosticParseKinds.td index 46a44418a3153..0bcbbf3498f9b 100644 --- a/clang/include/clang/Basic/DiagnosticParseKinds.td +++ b/clang/include/clang/Basic/DiagnosticParseKinds.td @@ -1684,6 +1684,11 @@ def err_import_in_wrong_fragment : Error< def err_export_empty : Error<"export declaration cannot be empty">; } +let CategoryName = "Reflection Issue" in { + +def err_cannot_reflect_entity : Error<"cannot reflect entity">; +} + let CategoryName = "Generics Issue" in { def err_objc_expected_type_parameter : Error< diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td index 4fbbc42273ba9..73a15b836dfce 100644 --- a/clang/include/clang/Basic/DiagnosticSemaKinds.td +++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -1,5 +1,7 @@ //==--- DiagnosticSemaKinds.td - libsema diagnostics ----------------------===// // +// Copyright 2024 Bloomberg Finance L.P. +// // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception @@ -3068,6 +3070,50 @@ def err_type_constraint_missing_arguments : Error< def err_placeholder_constraints_not_satisfied : Error< "deduced type %0 does not satisfy %1">; +// Reflection +def err_meta_type_constexpr : Error<"meta type variables must be constexpr">; +def err_reflect_non_constexpr : Error< + "expression operand of reflection operator must be a constant expression">; +def err_reflect_overload_set : Error< + "cannot take the reflection of an overload set">; +def err_template_arg_bad_reflection_kind : Error< + "reflection of this kind cannot appear as a template argument">; +def err_reflect_dependent_splice : Error< + "cannot take the reflection of a dependent splice expression">; + +def err_metafunction_empty_args : Error< + "__metafunction requires arguments">; +def err_metafunction_leading_arg_type : Error< + "leading argument of __metafunction must have integral type">; +def err_metafunction_not_constexpr : Error< + "arguments of __metafunction must be constant expressions">; +def err_unknown_metafunction : Error< + "leading argument to __metafunction does not identify a metafunction">; +def err_metafunction_arity : Error< + "expected between %0 and %1 arguments (%2 given)">; + +def err_splice_operand_not_reflection : Error< + "splice operand must be a reflection">; +def err_splice_operand_not_constexpr : Error< + "splice operand must be a constant expression">; +def err_unexpected_reflection_kind : Error< + "expected a reflection of %select{a|an|a}0 " + "%select{type|expression|namespace}0">; +def err_splice_global_scope_as_namespace : Error< + "splice of global scope can only appear in a nested-name-specifier">; +def err_member_access_splice_not_class_member : Error< + "use of splice as qualified member access requires the reflection to be a " + "member of a class">; +def err_unsupported_splice_kind : Error< + "splicing %0 %select{as template arguments is|is}1 %select{not|not yet}2 " + "%select{supported|implemented}2">; +def err_using_dependent_namespace : Error< + "dependent namespaces cannot appear in a using directive">; +def err_dependent_splice_implicit_member_reference : Error< + "cannot implicitly reference a class member through a splice">; +def note_dependent_splice_explicit_this_may_fix : Note< + "an explicit 'this' pointer may fix the problem">; + // C++11 char16_t/char32_t def warn_cxx98_compat_unicode_type : Warning< "'%0' type specifier is incompatible with C++98">, diff --git a/clang/include/clang/Basic/Features.def b/clang/include/clang/Basic/Features.def index fe4d1c4afcca6..178e379c14e77 100644 --- a/clang/include/clang/Basic/Features.def +++ b/clang/include/clang/Basic/Features.def @@ -294,6 +294,9 @@ EXTENSION(datasizeof, LangOpts.CPlusPlus) FEATURE(cxx_abi_relative_vtable, LangOpts.CPlusPlus && LangOpts.RelativeCXXABIVTables) +FEATURE(reflection, LangOpts.Reflection) +FEATURE(parameter_reflection, LangOpts.ParameterReflection) + // CUDA/HIP Features FEATURE(cuda_noinline_keyword, LangOpts.CUDA) EXTENSION(cuda_implicit_host_device_templates, LangOpts.CUDA && LangOpts.OffloadImplicitHostDeviceTemplates) diff --git a/clang/include/clang/Basic/LangOptions.def b/clang/include/clang/Basic/LangOptions.def index 8ef6700ecdc78..64b0b10c23ac5 100644 --- a/clang/include/clang/Basic/LangOptions.def +++ b/clang/include/clang/Basic/LangOptions.def @@ -293,6 +293,9 @@ LANGOPT(HIPStdParInterposeAlloc, 1, 0, "Replace allocations / deallocations with LANGOPT(OpenACC , 1, 0, "OpenACC Enabled") +LANGOPT(Reflection , 1, 0, "Experimental C++26 Reflection Enabled") +LANGOPT(ParameterReflection, 1, 0, "Augments C++26 Reflection with function parameter reflection") + LANGOPT(SizedDeallocation , 1, 0, "sized deallocation") LANGOPT(AlignedAllocation , 1, 0, "aligned allocation") LANGOPT(AlignedAllocationUnavailable, 1, 0, "aligned allocation functions are unavailable") diff --git a/clang/include/clang/Basic/Specifiers.h b/clang/include/clang/Basic/Specifiers.h index fb11e8212f8b6..78f4bdd59d272 100644 --- a/clang/include/clang/Basic/Specifiers.h +++ b/clang/include/clang/Basic/Specifiers.h @@ -87,6 +87,7 @@ namespace clang { TST_typeof_unqualType, // C23 typeof_unqual(type-name) TST_typeof_unqualExpr, // C23 typeof_unqual(expression) TST_decltype, // C++11 decltype + TST_type_splice, // C++2c splice of a reflection of a type (P2996) #define TRANSFORM_TYPE_TRAIT_DEF(_, Trait) TST_##Trait, #include "clang/Basic/TransformTypeTraits.def" TST_auto, // C++11 auto diff --git a/clang/include/clang/Basic/StmtNodes.td b/clang/include/clang/Basic/StmtNodes.td index b4e3ae573b95e..3320a85fafa04 100644 --- a/clang/include/clang/Basic/StmtNodes.td +++ b/clang/include/clang/Basic/StmtNodes.td @@ -148,6 +148,7 @@ def ExprWithCleanups : StmtNode; def CXXTemporaryObjectExpr : StmtNode; def CXXUnresolvedConstructExpr : StmtNode; def CXXDependentScopeMemberExpr : StmtNode; +def CXXDependentMemberSpliceExpr : StmtNode; def OverloadExpr : StmtNode; def UnresolvedLookupExpr : StmtNode; def UnresolvedMemberExpr : StmtNode; @@ -173,6 +174,14 @@ def CoyieldExpr : StmtNode; def ConceptSpecializationExpr : StmtNode; def RequiresExpr : StmtNode; +// C++2c Reflection expressions +def CXXReflectExpr : StmtNode; +def CXXMetafunctionExpr : StmtNode; +def CXXIndeterminateSpliceExpr : StmtNode; +def CXXExprSpliceExpr : StmtNode; +def StackLocationExpr : StmtNode; +def ValueOfLValueExpr : StmtNode; + // Obj-C Expressions. def ObjCStringLiteral : StmtNode; def ObjCBoxedExpr : StmtNode; diff --git a/clang/include/clang/Basic/TargetInfo.h b/clang/include/clang/Basic/TargetInfo.h index e1ef7454f0166..45f4daf36fc2d 100644 --- a/clang/include/clang/Basic/TargetInfo.h +++ b/clang/include/clang/Basic/TargetInfo.h @@ -1,5 +1,7 @@ //===--- TargetInfo.h - Expose information about the target -----*- C++ -*-===// // +// Copyright 2024 Bloomberg Finance L.P. +// // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception @@ -91,6 +93,7 @@ struct TransferrableTargetInfo { unsigned char FloatWidth, FloatAlign; unsigned char DoubleWidth, DoubleAlign; unsigned char LongDoubleWidth, LongDoubleAlign, Float128Align, Ibm128Align; + unsigned char MetaInfoWidth, MetaInfoAlign; unsigned char LargeArrayMinWidth, LargeArrayAlign; unsigned char LongWidth, LongAlign; unsigned char LongLongWidth, LongLongAlign; @@ -779,6 +782,10 @@ class TargetInfo : public TransferrableTargetInfo, unsigned getIbm128Align() const { return Ibm128Align; } const llvm::fltSemantics &getIbm128Format() const { return *Ibm128Format; } + /// getMetaInfoWidth/Align - Returns the size/align of meta::info. + unsigned getMetaInfoWidth() const { return MetaInfoWidth; } + unsigned getMetaInfoAlign() const { return MetaInfoAlign; } + /// Return the mangled code of long double. virtual const char *getLongDoubleMangling() const { return "e"; } diff --git a/clang/include/clang/Basic/TokenKinds.def b/clang/include/clang/Basic/TokenKinds.def index 800af0e6d0448..643644f168975 100644 --- a/clang/include/clang/Basic/TokenKinds.def +++ b/clang/include/clang/Basic/TokenKinds.def @@ -1,5 +1,7 @@ //===--- TokenKinds.def - C Family Token Kind Database ----------*- C++ -*-===// // +// Copyright 2024 Bloomberg Finance L.P. +// // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception @@ -242,6 +244,10 @@ PUNCTUATOR(periodstar, ".*") PUNCTUATOR(arrowstar, "->*") PUNCTUATOR(coloncolon, "::") +// C++2c Reflection Support (P2996) +PUNCTUATOR(l_splice, "[:") +PUNCTUATOR(r_splice, ":]") + // Objective C support. PUNCTUATOR(at, "@") @@ -415,6 +421,9 @@ CXX20_KEYWORD(constinit , 0) CXX20_KEYWORD(concept , 0) CXX20_KEYWORD(requires , 0) +// C++2c reflection keywords. +KEYWORD(__metafunction , KEYREFLECT) + // Not a CXX20_KEYWORD because it is disabled by -fno-char8_t. KEYWORD(char8_t , CHAR8SUPPORT) @@ -847,6 +856,10 @@ ANNOTATION(decltype) // annotation for a decltype expression, ANNOTATION(pack_indexing_type) // annotation for an indexed pack of type, // e.g., "T...[expr]" +ANNOTATION(splice) // annotation for a splice of a reflection whose kind + // is yet to be determined (e.g., expression, type, + // namespace, template). + // Annotation for #pragma unused(...) // For each argument inside the parentheses the pragma handler will produce // one 'pragma_unused' annotation token followed by the argument token. diff --git a/clang/include/clang/Basic/TypeNodes.td b/clang/include/clang/Basic/TypeNodes.td index fee49cf4326df..1f1e64a1ea443 100644 --- a/clang/include/clang/Basic/TypeNodes.td +++ b/clang/include/clang/Basic/TypeNodes.td @@ -104,6 +104,7 @@ def InjectedClassNameType : TypeNode, AlwaysDependent, LeafType; def DependentNameType : TypeNode, AlwaysDependent; def DependentTemplateSpecializationType : TypeNode, AlwaysDependent; def PackExpansionType : TypeNode, AlwaysDependent; +def ReflectionSpliceType : TypeNode, NeverCanonicalUnlessDependent; def PackIndexingType : TypeNode, NeverCanonicalUnlessDependent; def ObjCTypeParamType : TypeNode, NeverCanonical; def ObjCObjectType : TypeNode; diff --git a/clang/include/clang/Driver/Options.td b/clang/include/clang/Driver/Options.td index f745e573eb268..abad085759095 100644 --- a/clang/include/clang/Driver/Options.td +++ b/clang/include/clang/Driver/Options.td @@ -1,5 +1,7 @@ //===--- Options.td - Options for clang -----------------------------------===// // +// Copyright 2024 Bloomberg Finance L.P. +// // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception @@ -3347,6 +3349,16 @@ defm relaxed_template_template_args : BoolFOption<"relaxed-template-template-arg PosFlag, NegFlag>; +defm reflection : BoolFOption<"reflection", + LangOpts<"Reflection">, DefaultFalse, + PosFlag, + NegFlag>; +defm parameter_reflection : BoolFOption<"parameter-reflection", + LangOpts<"ParameterReflection">, DefaultFalse, + PosFlag, + NegFlag>; defm sized_deallocation : BoolFOption<"sized-deallocation", LangOpts<"SizedDeallocation">, DefaultFalse, PosFlag ParenCount || - P.BracketCount > BracketCount || P.BraceCount > BraceCount; + P.BracketCount > BracketCount || P.BraceCount > BraceCount || + P.SpliceCount > SpliceCount; } }; @@ -392,7 +396,8 @@ class Parser : public CodeCompletionHandler { } } else { Locs.push_back({TemplateName, LessLoc, Prio, - P.ParenCount, P.BracketCount, P.BraceCount}); + P.ParenCount, P.BracketCount, + P.BraceCount, P.SpliceCount}); } } @@ -585,6 +590,10 @@ class Parser : public CodeCompletionHandler { bool isTokenBrace() const { return Tok.isOneOf(tok::l_brace, tok::r_brace); } + /// isTokenSplice - Return true if the cur token is "[:" or ":]". + bool isTokenSplice() const { + return Tok.isOneOf(tok::l_splice, tok::r_splice); + } /// isTokenStringLiteral - True if this token is a string-literal. bool isTokenStringLiteral() const { return tok::isStringLiteral(Tok.getKind()); @@ -647,6 +656,22 @@ class Parser : public CodeCompletionHandler { return PrevTokLocation; } + /// ConsumeSplice - This consume methods keeps the splice count up-to-date. + SourceLocation ConsumeSplice() { + assert(isTokenSplice() && "wrong consume method"); + if (Tok.getKind() == tok::l_splice) + ++SpliceCount; + else if (SpliceCount) { + AngleBrackets.clear(*this); + --SpliceCount; // Don't let unbalanced :]'s drive the count negative. + } + + PrevTokLocation = Tok.getLocation(); + PP.Lex(Tok); + return PrevTokLocation; + } + + /// ConsumeBrace - This consume method keeps the brace count up-to-date. /// SourceLocation ConsumeBrace() { @@ -996,7 +1021,8 @@ class Parser : public CodeCompletionHandler { PreferredTypeBuilder PrevPreferredType; Token PrevTok; size_t PrevTentativelyDeclaredIdentifierCount; - unsigned short PrevParenCount, PrevBracketCount, PrevBraceCount; + unsigned short PrevParenCount, PrevBracketCount; + unsigned short PrevBraceCount, PrevSpliceCount; bool isActive; public: @@ -1008,6 +1034,7 @@ class Parser : public CodeCompletionHandler { PrevParenCount = P.ParenCount; PrevBracketCount = P.BracketCount; PrevBraceCount = P.BraceCount; + PrevSpliceCount = P.SpliceCount; P.PP.EnableBacktrackAtThisPos(); isActive = true; } @@ -1027,6 +1054,7 @@ class Parser : public CodeCompletionHandler { PrevTentativelyDeclaredIdentifierCount); P.ParenCount = PrevParenCount; P.BracketCount = PrevBracketCount; + P.SpliceCount = PrevSpliceCount; P.BraceCount = PrevBraceCount; isActive = false; } @@ -2250,6 +2278,7 @@ class Parser : public CodeCompletionHandler { DSC_condition, // condition declaration context DSC_association, // A _Generic selection expression's type association DSC_new, // C++ new expression + DSC_reflect_operator, // C++2c reflect operator (P2996) }; /// Is this a context in which we are parsing just a type-specifier (or @@ -2263,6 +2292,7 @@ class Parser : public CodeCompletionHandler { case DeclSpecContext::DSC_top_level: case DeclSpecContext::DSC_objc_method_result: case DeclSpecContext::DSC_condition: + case DeclSpecContext::DSC_reflect_operator: return false; case DeclSpecContext::DSC_template_type_arg: @@ -2321,6 +2351,7 @@ class Parser : public CodeCompletionHandler { case DeclSpecContext::DSC_conv_operator: case DeclSpecContext::DSC_template_arg: case DeclSpecContext::DSC_new: + case DeclSpecContext::DSC_reflect_operator: return AllowDefiningTypeSpec::No; } llvm_unreachable("Missing DeclSpecContext case"); @@ -2345,6 +2376,7 @@ class Parser : public CodeCompletionHandler { case DeclSpecContext::DSC_conv_operator: case DeclSpecContext::DSC_template_arg: case DeclSpecContext::DSC_new: + case DeclSpecContext::DSC_reflect_operator: return false; } @@ -2365,6 +2397,7 @@ class Parser : public CodeCompletionHandler { case DeclSpecContext::DSC_association: case DeclSpecContext::DSC_conv_operator: case DeclSpecContext::DSC_new: + case DeclSpecContext::DSC_reflect_operator: return true; case DeclSpecContext::DSC_objc_method_result: @@ -2396,6 +2429,7 @@ class Parser : public CodeCompletionHandler { case DeclSpecContext::DSC_template_arg: case DeclSpecContext::DSC_conv_operator: case DeclSpecContext::DSC_association: + case DeclSpecContext::DSC_reflect_operator: return ImplicitTypenameContext::No; } llvm_unreachable("Missing DeclSpecContext case"); @@ -2569,6 +2603,7 @@ class Parser : public CodeCompletionHandler { TypeIdAsTemplateArgument, TypeIdInTrailingReturnType, TypeIdAsGenericSelectionArgument, + TypeIdAsReflectionOperand, }; /// isTypeIdInParens - Assumes that a '(' was parsed and now we want to know @@ -3227,6 +3262,8 @@ class Parser : public CodeCompletionHandler { }; using InnerNamespaceInfoList = llvm::SmallVector; + Decl *ParseNamespaceName(CXXScopeSpec &SS, SourceLocation &IdentLoc); + void ParseInnerNamespace(const InnerNamespaceInfoList &InnerNSs, unsigned int index, SourceLocation &InlineLoc, ParsedAttributes &attrs, @@ -3723,7 +3760,9 @@ class Parser : public CodeCompletionHandler { bool ParseTemplateArgumentList(TemplateArgList &TemplateArgs, TemplateTy Template, SourceLocation OpenLoc); ParsedTemplateArgument ParseTemplateTemplateArgument(); + ParsedTemplateArgument ParseIndeterminateSpliceTemplateArgument(); ParsedTemplateArgument ParseTemplateArgument(); + ParsedTemplateArgument ParseTemplateReflectOperand(); DeclGroupPtrTy ParseExplicitInstantiation(DeclaratorContext Context, SourceLocation ExternLoc, SourceLocation TemplateLoc, @@ -3775,6 +3814,17 @@ class Parser : public CodeCompletionHandler { ExprResult ParseArrayTypeTrait(); ExprResult ParseExpressionTrait(); + //===--------------------------------------------------------------------===// + // C++2c: Reflection [P2996] + ExprResult ParseCXXReflectExpression(); + ExprResult ParseCXXMetafunctionExpression(); + + bool ParseCXXIndeterminateSplice(); + + TypeResult ParseCXXSpliceAsType(bool AllowDependent, bool Complain); + ExprResult ParseCXXSpliceAsExpr(bool AllowMemberReference); + DeclResult ParseCXXSpliceAsNamespace(); + //===--------------------------------------------------------------------===// // Preprocessor code-completion pass-through void CodeCompleteDirective(bool InConditional) override; diff --git a/clang/include/clang/Parse/RAIIObjectsForParser.h b/clang/include/clang/Parse/RAIIObjectsForParser.h index f4fa518ef27d0..fd2bf710b3f23 100644 --- a/clang/include/clang/Parse/RAIIObjectsForParser.h +++ b/clang/include/clang/Parse/RAIIObjectsForParser.h @@ -1,5 +1,7 @@ //===--- RAIIObjectsForParser.h - RAII helpers for the parser ---*- C++ -*-===// // +// Copyright 2024 Bloomberg Finance L.P. +// // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception @@ -377,17 +379,18 @@ namespace clang { /// after declaration/statement parsing, even when there's a parsing error. class ParenBraceBracketBalancer { Parser &P; - unsigned short ParenCount, BracketCount, BraceCount; + unsigned short ParenCount, BracketCount, BraceCount, SpliceCount; public: ParenBraceBracketBalancer(Parser &p) : P(p), ParenCount(p.ParenCount), BracketCount(p.BracketCount), - BraceCount(p.BraceCount) { } + BraceCount(p.BraceCount), SpliceCount(p.SpliceCount) { } ~ParenBraceBracketBalancer() { P.AngleBrackets.clear(P); P.ParenCount = ParenCount; P.BracketCount = BracketCount; P.BraceCount = BraceCount; + P.SpliceCount = SpliceCount; } }; @@ -428,6 +431,7 @@ namespace clang { case tok::l_brace: return P.BraceCount; case tok::l_square: return P.BracketCount; case tok::l_paren: return P.ParenCount; + case tok::l_splice: return P.SpliceCount; default: llvm_unreachable("Wrong token kind"); } } @@ -456,6 +460,11 @@ namespace clang { Close = tok::r_square; Consumer = &Parser::ConsumeBracket; break; + + case tok::l_splice: + Close = tok::r_splice; + Consumer = &Parser::ConsumeSplice; + break; } } diff --git a/clang/include/clang/Sema/DeclSpec.h b/clang/include/clang/Sema/DeclSpec.h index a176159707486..2881524029e70 100644 --- a/clang/include/clang/Sema/DeclSpec.h +++ b/clang/include/clang/Sema/DeclSpec.h @@ -1,5 +1,7 @@ //===--- DeclSpec.h - Parsed declaration specifiers -------------*- C++ -*-===// // +// Copyright 2024 Bloomberg Finance L.P. +// // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception @@ -172,6 +174,19 @@ class CXXScopeSpec { void MakeSuper(ASTContext &Context, CXXRecordDecl *RD, SourceLocation SuperLoc, SourceLocation ColonColonLoc); + /// Turns this (empty) nested-name-specifier into a specifier having a single + /// component of indeterminate splice kind. + /// + /// \param Context The AST context in which this nested-name-specifier + /// resides. + /// + /// \param Expr The splice expression. + /// + /// \param ColonColonLoc The location of the trailing '::'. + void MakeIndeterminateSplice(ASTContext &Context, + CXXIndeterminateSpliceExpr *Expr, + SourceLocation ColonColonLoc); + /// Make a new nested-name-specifier from incomplete source-location /// information. /// @@ -308,6 +323,7 @@ class DeclSpec { static const TST TST_typeof_unqualType = clang::TST_typeof_unqualType; static const TST TST_typeof_unqualExpr = clang::TST_typeof_unqualExpr; static const TST TST_decltype = clang::TST_decltype; + static const TST TST_type_splice = clang::TST_type_splice; static const TST TST_decltype_auto = clang::TST_decltype_auto; static const TST TST_typename_pack_indexing = clang::TST_typename_pack_indexing; @@ -453,7 +469,7 @@ class DeclSpec { } static bool isExprRep(TST T) { return T == TST_typeofExpr || T == TST_typeof_unqualExpr || - T == TST_decltype || T == TST_bitint; + T == TST_decltype || T == TST_type_splice || T == TST_bitint; } static bool isTemplateIdRep(TST T) { return (T == TST_auto || T == TST_decltype_auto); @@ -1874,7 +1890,8 @@ enum class DeclaratorContext { AliasDecl, // C++11 alias-declaration. AliasTemplate, // C++11 alias-declaration template. RequiresExpr, // C++2a requires-expression. - Association // C11 _Generic selection expression association. + Association, // C11 _Generic selection expression association. + ReflectOperator // C++2c reflect operator (P2996). }; // Describes whether the current context is a context where an implicit @@ -2161,6 +2178,7 @@ class Declarator { case DeclaratorContext::TrailingReturnVar: case DeclaratorContext::RequiresExpr: case DeclaratorContext::Association: + case DeclaratorContext::ReflectOperator: return true; } llvm_unreachable("unknown context kind!"); @@ -2201,6 +2219,7 @@ class Declarator { case DeclaratorContext::TrailingReturn: case DeclaratorContext::TrailingReturnVar: case DeclaratorContext::Association: + case DeclaratorContext::ReflectOperator: return false; } llvm_unreachable("unknown context kind!"); @@ -2245,6 +2264,7 @@ class Declarator { case DeclaratorContext::TrailingReturn: case DeclaratorContext::TrailingReturnVar: case DeclaratorContext::Association: + case DeclaratorContext::ReflectOperator: return false; } llvm_unreachable("unknown context kind!"); @@ -2302,6 +2322,7 @@ class Declarator { case DeclaratorContext::TrailingReturn: case DeclaratorContext::RequiresExpr: case DeclaratorContext::Association: + case DeclaratorContext::ReflectOperator: return false; } llvm_unreachable("unknown context kind!"); @@ -2541,6 +2562,7 @@ class Declarator { case DeclaratorContext::TrailingReturnVar: case DeclaratorContext::RequiresExpr: case DeclaratorContext::Association: + case DeclaratorContext::ReflectOperator: return false; } llvm_unreachable("unknown context kind!"); @@ -2583,6 +2605,7 @@ class Declarator { case DeclaratorContext::SelectionInit: case DeclaratorContext::Condition: case DeclaratorContext::TemplateArg: + case DeclaratorContext::ReflectOperator: return true; } diff --git a/clang/include/clang/Sema/Lookup.h b/clang/include/clang/Sema/Lookup.h index 2f2f2607a937f..466436d495181 100644 --- a/clang/include/clang/Sema/Lookup.h +++ b/clang/include/clang/Sema/Lookup.h @@ -1,5 +1,7 @@ //===- Lookup.h - Classes for name lookup -----------------------*- C++ -*-===// // +// Copyright 2024 Bloomberg Finance L.P. +// // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception @@ -563,6 +565,9 @@ class LookupResult { NamedDecl *getFoundDecl() const { assert(getResultKind() == Found && "getFoundDecl called on non-unique result"); + if (getSema().isReflectionContext() && isa(*begin())) + return *begin(); + return (*begin())->getUnderlyingDecl(); } @@ -754,8 +759,7 @@ class LookupResult { private: void diagnoseAccess() { - if (!isAmbiguous() && isClassLookup() && - getSema().getLangOpts().AccessControl) + if (!isAmbiguous() && isClassLookup() && getSema().languageAccessControl()) getSema().CheckLookupAccess(*this); } diff --git a/clang/include/clang/Sema/Metafunction.h b/clang/include/clang/Sema/Metafunction.h new file mode 100644 index 0000000000000..b2d5cc0ea731d --- /dev/null +++ b/clang/include/clang/Sema/Metafunction.h @@ -0,0 +1,87 @@ +//===-- Metafunction.h - Classes for representing metafunctions--*- C++ -*-===// +// +// Copyright 2024 Bloomberg Finance L.P. +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +/// +/// \file +/// \brief Defines facilities for representing functions involving reflections. +/// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_SEMA_METAFUNCTION_H +#define LLVM_CLANG_SEMA_METAFUNCTION_H + +#include "clang/AST/ExprCXX.h" +#include "clang/AST/Type.h" +#include + + +namespace clang { + +class APValue; +class Sema; + +class Metafunction { +public: + // Enumerators identifying the return-type of a metafunction. + enum ResultKind : unsigned { + MFRK_bool, + MFRK_cstring, + MFRK_metaInfo, + MFRK_sizeT, + MFRK_sourceLoc, + MFRK_spliceFromArg, + }; + + using EvaluateFn = CXXMetafunctionExpr::EvaluateFn; + +private: + using impl_fn_t = bool (*)(APValue &Result, + Sema &SemaRef, + EvaluateFn Evaluator, + QualType ResultType, + SourceRange Range, + ArrayRef Args); + + ResultKind Kind; + unsigned MinArgs; + unsigned MaxArgs; + impl_fn_t ImplFn; + +public: + constexpr Metafunction(ResultKind ResultKind, + unsigned MinArgs, + unsigned MaxArgs, + impl_fn_t ImplFn) + : Kind(ResultKind), MinArgs(MinArgs), MaxArgs(MaxArgs), + ImplFn(ImplFn) { } + + ResultKind getResultKind() const { + return Kind; + } + + unsigned getMinArgs() const { + return MinArgs; + } + + unsigned getMaxArgs() const { + return MaxArgs; + } + + bool evaluate(APValue &Result, Sema &S, EvaluateFn Evaluator, + QualType ResultType, SourceRange Range, + ArrayRef Args) const; + + // Get a pointer to the metafunction with the given ID. + // Returns true in the case of error (i.e., no such metafunction exists). + static bool Lookup(unsigned ID, const Metafunction *&result); +}; + +} // namespace clang + +#endif diff --git a/clang/include/clang/Sema/Ownership.h b/clang/include/clang/Sema/Ownership.h index 0752f5de7e334..85781dceb6828 100644 --- a/clang/include/clang/Sema/Ownership.h +++ b/clang/include/clang/Sema/Ownership.h @@ -264,6 +264,7 @@ using MultiTemplateParamsArg = MutableArrayRef; inline ExprResult ExprError() { return ExprResult(true); } inline StmtResult StmtError() { return StmtResult(true); } inline TypeResult TypeError() { return TypeResult(true); } +inline DeclResult DeclError() { return DeclResult(true); } inline ExprResult ExprError(const StreamingDiagnostic &) { return ExprError(); } inline StmtResult StmtError(const StreamingDiagnostic &) { return StmtError(); } diff --git a/clang/include/clang/Sema/ParsedTemplate.h b/clang/include/clang/Sema/ParsedTemplate.h index 65182d57246ae..fcaf557375f81 100644 --- a/clang/include/clang/Sema/ParsedTemplate.h +++ b/clang/include/clang/Sema/ParsedTemplate.h @@ -35,7 +35,9 @@ namespace clang { /// A non-type template parameter, stored as an expression. NonType, /// A template template argument, stored as a template name. - Template + Template, + /// An indeterminate splice argument, stored as an expression. + IndeterminateSplice, }; /// Build an empty template argument. @@ -89,6 +91,12 @@ namespace clang { return ParsedTemplateTy::getFromOpaquePtr(Arg); } + CXXIndeterminateSpliceExpr *getAsIndeterminateSplice() const { + assert(Kind == IndeterminateSplice && + "Not an indeterminate splice argument"); + return static_cast(Arg); + } + /// Retrieve the location of the template argument. SourceLocation getLocation() const { return Loc; } diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h index 3fe2b6dd422a5..b3ffb86e96bce 100644 --- a/clang/include/clang/Sema/Sema.h +++ b/clang/include/clang/Sema/Sema.h @@ -1,5 +1,7 @@ //===--- Sema.h - Semantic Analysis & AST Building --------------*- C++ -*-===// // +// Copyright 2024 Bloomberg Finance L.P. +// // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception @@ -201,6 +203,7 @@ class TypeAliasDecl; class TypedefDecl; class TypedefNameDecl; class TypeLoc; +class TypeLocBuilder; class TypoCorrectionConsumer; class UnqualifiedId; class UnresolvedLookupExpr; @@ -468,6 +471,7 @@ class Sema final : public SemaBase { // 38. CUDA (SemaCUDA.cpp) // 39. OpenMP Directives and Clauses (SemaOpenMP.cpp) // 40. SYCL Constructs (SemaSYCL.cpp) + // 41. P2996 Reflection Constructs (SemaReflection.cpp) /// \name Semantic Analysis /// Implementations are in Sema.cpp @@ -491,6 +495,10 @@ class Sema final : public SemaBase { virtual void anchor(); const LangOptions &getLangOpts() const { return LangOpts; } + bool languageAccessControl() { + return getLangOpts().AccessControl && !EllideAccessControl; + } + OpenCLOptions &getOpenCLOptions() { return OpenCLFeatures; } FPOptions &getCurFPFeatures() { return CurFPFeatures; } @@ -985,6 +993,23 @@ class Sema final : public SemaBase { std::optional> CachedDarwinSDKInfo; bool WarnedDarwinSDKInfoMissing = false; + bool SuppressDiagnostics = false; + +public: + class SuppressDiagnosticsRAII { + Sema &S; + bool PreviousValue; + + public: + SuppressDiagnosticsRAII(Sema &S, bool NewValue = true) + : S(S), PreviousValue(S.SuppressDiagnostics) { + S.SuppressDiagnostics = NewValue; + } + + ~SuppressDiagnosticsRAII() { S.SuppressDiagnostics = PreviousValue; } + }; + +private: Sema(const Sema &) = delete; void operator=(const Sema &) = delete; @@ -3844,6 +3869,10 @@ class Sema final : public SemaBase { NamespaceDecl *getStdNamespace() const; NamespaceDecl *getOrCreateStdNamespace(); + NamespaceDecl *lookupStdMetaNamespace(); + + RecordDecl *lookupStdSourceLocationImpl(SourceLocation Loc); + CXXRecordDecl *getStdBadAlloc() const; EnumDecl *getStdAlignValT() const; @@ -3884,11 +3913,21 @@ class Sema final : public SemaBase { /// defined in [dcl.init.list]p2. bool isInitListConstructor(const FunctionDecl *Ctor); + /// Resolve SS and Id as a namespace name. Returns nullptr if the pair does + /// not denote a namespace. + Decl *ActOnNamespaceName(Scope *CurScope, CXXScopeSpec &SS, + IdentifierInfo *Id, SourceLocation IdLoc); + Decl *ActOnUsingDirective(Scope *CurScope, SourceLocation UsingLoc, SourceLocation NamespcLoc, CXXScopeSpec &SS, SourceLocation IdentLoc, IdentifierInfo *NamespcName, const ParsedAttributesView &AttrList); + Decl *ActOnUsingDirective(Scope *CurScope, SourceLocation UsingLoc, + SourceLocation NamespaceLoc, CXXScopeSpec &SS, + SourceLocation IdentLoc, NamedDecl *Named, + NamespaceDecl *NS, + const ParsedAttributesView &AttrList); void PushUsingDirective(Scope *S, UsingDirectiveDecl *UDir); @@ -3896,6 +3935,13 @@ class Sema final : public SemaBase { SourceLocation AliasLoc, IdentifierInfo *Alias, CXXScopeSpec &SS, SourceLocation IdentLoc, IdentifierInfo *Ident); + Decl *ActOnNamespaceAliasDef(Scope *CurScope, + SourceLocation NamespaceLoc, + SourceLocation AliasLoc, + IdentifierInfo *Alias, + CXXScopeSpec &SS, + SourceLocation IdentLoc, + NamedDecl *ND); void FilterUsingLookup(Scope *S, LookupResult &lookup); void HideUsingShadowDecl(Scope *S, UsingShadowDecl *Shadow); @@ -3953,6 +3999,11 @@ class Sema final : public SemaBase { SourceLocation EnumLoc, SourceLocation IdentLoc, IdentifierInfo &II, CXXScopeSpec *SS = nullptr); + Decl *ActOnUsingEnumDeclaration(Scope *CurScope, AccessSpecifier AS, + SourceLocation UsingLoc, + SourceLocation EnumLoc, + SourceLocation IdentLoc, QualType EnumTy, + TypeSourceInfo *TSI); Decl *ActOnAliasDeclaration(Scope *CurScope, AccessSpecifier AS, MultiTemplateParamsArg TemplateParams, SourceLocation UsingLoc, UnqualifiedId &Name, @@ -4794,6 +4845,9 @@ class Sema final : public SemaBase { /// The C++ "std" namespace, where the standard library resides. LazyDeclPtr StdNamespace; + /// The C++ "std::experimental::meta" namespace. + NamespaceDecl *StdMetaNamespace; + /// The C++ "std::initializer_list" template, which is defined in /// \. ClassTemplateDecl *StdInitializerList; @@ -4989,7 +5043,11 @@ class Sema final : public SemaBase { /// we would like to provide diagnostics (e.g., passing non-POD arguments /// through varargs) but do not want to mark declarations as "referenced" /// until the default argument is used. - PotentiallyEvaluatedIfUsed + PotentiallyEvaluatedIfUsed, + + /// The current expression and its subexpressions occur within an + /// unevaluated reflection operand (C++26 P2996). + ReflectionContext, }; /// Store a set of either DeclRefExprs or MemberExprs that contain a reference @@ -5145,7 +5203,8 @@ class Sema final : public SemaBase { bool isUnevaluated() const { return Context == ExpressionEvaluationContext::Unevaluated || Context == ExpressionEvaluationContext::UnevaluatedAbstract || - Context == ExpressionEvaluationContext::UnevaluatedList; + Context == ExpressionEvaluationContext::UnevaluatedList || + Context == ExpressionEvaluationContext::ReflectionContext; } bool isConstantEvaluated() const { @@ -5174,6 +5233,10 @@ class Sema final : public SemaBase { ExpressionEvaluationContext::ImmediateFunctionContext && InDiscardedStatement); } + + bool isReflectionContext() const { + return Context == ExpressionEvaluationContext::ReflectionContext; + } }; const ExpressionEvaluationContextRecord ¤tEvaluationContext() const { @@ -6332,6 +6395,12 @@ class Sema final : public SemaBase { return currentEvaluationContext().isUnevaluated(); } + /// Determines whether we are currently in a context assembling a reflect + /// expression. This is akin to unevaluated, but has some special rules. + bool isReflectionContext() const { + return currentEvaluationContext().isReflectionContext(); + } + bool isImmediateFunctionContext() const { return currentEvaluationContext().isImmediateFunctionContext(); } @@ -9329,6 +9398,10 @@ class Sema final : public SemaBase { BuildExpressionFromNonTypeTemplateArgument(const TemplateArgument &Arg, SourceLocation Loc); + ExprResult + BuildExpressionFromReflectionTemplateArgument(const TemplateArgument &Arg, + SourceLocation Loc); + /// Enumeration describing how template parameter lists are compared /// for equality. enum TemplateParameterListEqualKind { @@ -14554,6 +14627,140 @@ class Sema final : public SemaBase { ValueDecl *DeclToCheck); ///@} + + // + // + // ------------------------------------------------------------------------- + // + // + + /// \name P2996 Reflection Constructs + /// Implementations are in SemaReflect.cpp + ///@{ + +public: + ExprResult ActOnCXXReflectExpr(SourceLocation OpLoc, TypeResult T); + ExprResult ActOnCXXReflectExpr(SourceLocation OpLoc, Expr *E); + ExprResult ActOnCXXReflectExpr(SourceLocation OpLoc, + SourceLocation ArgLoc, Decl *D); + ExprResult ActOnCXXReflectExpr(SourceLocation OpLoc, + ParsedTemplateArgument Template); + ExprResult ActOnCXXReflectExpr(SourceLocation OpLoc, + CXXIndeterminateSpliceExpr *E); + + ExprResult ActOnCXXMetafunction(SourceLocation KwLoc, + SourceLocation LParenLoc, + SmallVectorImpl &Args, + SourceLocation RParenLoc); + ExprResult ActOnCXXIndeterminateSpliceExpr(SourceLocation LSpliceLoc, + Expr *Operand, + SourceLocation RSpliceLoc); + TypeResult ActOnCXXSpliceExpectingType(SourceLocation LSplice, + Expr *Operand, + SourceLocation RSplice, + bool Complain); + ExprResult ActOnCXXSpliceExpectingExpr(SourceLocation LSplice, + Expr *Operand, + SourceLocation RSplice, + bool AllowMemberReference); + DeclResult ActOnCXXSpliceExpectingNamespace(SourceLocation LSplice, + Expr *Operand, + SourceLocation RSplice); + ParsedTemplateArgument + ActOnTemplateIndeterminateSpliceArgument(CXXIndeterminateSpliceExpr *Splice); + + bool ActOnCXXNestedNameSpecifierReflectionSplice( + CXXScopeSpec &SS, CXXIndeterminateSpliceExpr *Splice, + SourceLocation ColonColonLoc); + + ExprResult ActOnMemberAccessExpr(Scope *S, Expr *Base, + SourceLocation OpLoc, + tok::TokenKind OpKind, + CXXExprSpliceExpr *RHS, + SourceLocation TemplateKWLoc); + + ExprResult BuildCXXReflectExpr(SourceLocation OperatorLoc, + SourceLocation OperandLoc, QualType T); + ExprResult BuildCXXReflectExpr(SourceLocation OperatorLoc, Expr *E); + ExprResult BuildCXXReflectExpr(SourceLocation OperatorLoc, + SourceLocation OperandLoc, Decl *D); + ExprResult BuildCXXReflectExpr(SourceLocation OperatorLoc, + UnresolvedLookupExpr *E); + ExprResult BuildCXXReflectExpr(SourceLocation OperatorLoc, + SourceLocation OperandLoc, + TemplateName Template); + + ExprResult BuildCXXMetafunctionExpr(SourceLocation KwLoc, + SourceLocation LParenLoc, + SourceLocation RParenLoc, + unsigned MetaFnID, + const CXXMetafunctionExpr::ImplFn &Impl, + SmallVectorImpl &Args); + + ExprResult BuildCXXIndeterminateSpliceExpr(SourceLocation LSpliceLoc, + Expr *Operand, + SourceLocation RSpliceLoc); + QualType BuildReflectionSpliceType(SourceLocation LSplice, + Expr *Operand, + SourceLocation RSplice, + bool Complain); + QualType BuildReflectionSpliceTypeLoc(TypeLocBuilder &TLB, + SourceLocation LSpliceLoc, + Expr *E, SourceLocation RSpliceLoc, + bool Complain); + ExprResult BuildReflectionSpliceExpr(SourceLocation LSplice, Expr *Operand, + SourceLocation RSplice, + bool AllowMemberReference); + DeclResult BuildReflectionSpliceNamespace(SourceLocation LSplice, + Expr *Operand, + SourceLocation RSplice); + + ExprResult BuildMemberReferenceExpr(Scope *S, Expr *Base, + SourceLocation OpLoc, + tok::TokenKind OpKind, + CXXExprSpliceExpr *RHS, + SourceLocation TemplateKWLoc); + ExprResult BuildDependentMemberSpliceExpr(Expr *Base, SourceLocation OpLoc, + bool IsArrow, + CXXExprSpliceExpr *RHS); + + DeclContext *TryFindDeclContextOf(const Expr *E); + + +private: + // Lambdas having bound references to this Sema object, used to evaluate + // metafunction (C++26, P2996) at constant evaluation time. + llvm::SmallDenseMap> + MetafunctionImplCbs; + + // Whether to elide access control when checking access to class members. + bool EllideAccessControl {false}; + + class AccessControlScopeGuard final { + public: + AccessControlScopeGuard(const AccessControlScopeGuard&) = delete; + AccessControlScopeGuard(AccessControlScopeGuard&&) = delete; + AccessControlScopeGuard& operator=(const AccessControlScopeGuard&) = delete; + AccessControlScopeGuard& operator=(AccessControlScopeGuard&&) = delete; + + // Sets EllideAccessControl to the new override value & keeps the + // previous one, so we can revert when the scope guard exits + explicit AccessControlScopeGuard(Sema &S, bool ellideAccessControlOverride) + : S_{S} + , previousEllideAccessControl_{S_.EllideAccessControl} { + S_.EllideAccessControl = ellideAccessControlOverride; + } + + ~AccessControlScopeGuard() { + S_.EllideAccessControl = previousEllideAccessControl_; + } + + private: + Sema &S_; + bool previousEllideAccessControl_ {false}; + }; + + ///@} }; DeductionFailureInfo diff --git a/clang/include/clang/Sema/Template.h b/clang/include/clang/Sema/Template.h index ce44aca797b0f..276c5701ae15f 100644 --- a/clang/include/clang/Sema/Template.h +++ b/clang/include/clang/Sema/Template.h @@ -14,6 +14,7 @@ #include "clang/AST/DeclTemplate.h" #include "clang/AST/DeclVisitor.h" +#include "clang/AST/Reflection.h" #include "clang/AST/TemplateBase.h" #include "clang/AST/Type.h" #include "clang/Basic/LLVM.h" @@ -345,6 +346,12 @@ enum class TemplateSubstitutionKind : char { : TemplateArgument(Ctx, Value, ValueType), DeducedFromArrayBound(DeducedFromArrayBound) {} + /// Construct an integral non-type template argument that + /// has been deduced, possibly from an array bound. + DeducedTemplateArgument(ASTContext &Ctx, const ReflectionValue &Value) + : TemplateArgument(Ctx, Value), + DeducedFromArrayBound(false) {} + /// For a non-type template argument, determine whether the /// template argument was deduced from an array bound. bool wasDeducedFromArrayBound() const { return DeducedFromArrayBound; } diff --git a/clang/include/clang/Serialization/ASTBitCodes.h b/clang/include/clang/Serialization/ASTBitCodes.h index f762116fea956..07a7b17a23d45 100644 --- a/clang/include/clang/Serialization/ASTBitCodes.h +++ b/clang/include/clang/Serialization/ASTBitCodes.h @@ -1076,6 +1076,9 @@ enum PredefinedTypeIDs { /// \brief The '__ibm128' type PREDEF_TYPE_IBM128_ID = 74, + /// \brief The 'std::meta::info' type + PREDEF_TYPE_META_INFO_ID = 75, + /// OpenCL image types with auto numeration #define IMAGE_TYPE(ImgType, Id, SingletonId, Access, Suffix) \ PREDEF_TYPE_##Id##_ID, @@ -1104,7 +1107,7 @@ enum PredefinedTypeIDs { /// /// Type IDs for non-predefined types will start at /// NUM_PREDEF_TYPE_IDs. -const unsigned NUM_PREDEF_TYPE_IDS = 502; +const unsigned NUM_PREDEF_TYPE_IDS = 503; // Ensure we do not overrun the predefined types we reserved // in the enum PredefinedTypeIDs above. @@ -2013,6 +2016,15 @@ enum StmtCode { EXPR_COYIELD, EXPR_DEPENDENT_COAWAIT, + // C++2c reflection (P2996) + EXPR_REFLECT, + EXPR_METAFUNCTION, + EXPR_SPLICE, + EXPR_EXPR_SPLICE, + EXPR_DEPENDENT_MEMBER_SPLICE, + EXPR_STACK_LOCATION, + EXPR_VALUE_OF_LVALUE, + // FixedPointLiteral EXPR_FIXEDPOINT_LITERAL, diff --git a/clang/include/clang/Serialization/ASTRecordWriter.h b/clang/include/clang/Serialization/ASTRecordWriter.h index 1feb8fcbacf77..90017bfdb5a39 100644 --- a/clang/include/clang/Serialization/ASTRecordWriter.h +++ b/clang/include/clang/Serialization/ASTRecordWriter.h @@ -1,5 +1,7 @@ //===- ASTRecordWriter.h - Helper classes for writing AST -------*- C++ -*-===// // +// Copyright 2024 Bloomberg Finance L.P. +// // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception @@ -180,6 +182,11 @@ class ASTRecordWriter /// Emit an APvalue. void AddAPValue(const APValue &Value) { writeAPValue(Value); } + /// Emit a ReflectionValue. + void AddReflectionValue(const ReflectionValue &Value) { + writeReflectionValue(Value); + } + /// Emit a reference to an identifier. void AddIdentifierRef(const IdentifierInfo *II) { return Writer->AddIdentifierRef(II, *Record); @@ -203,6 +210,10 @@ class ASTRecordWriter /// Emit a set of C++ base specifiers. void AddCXXBaseSpecifiers(ArrayRef Bases); + void writeCXXBaseSpecifierRef(const CXXBaseSpecifier *S) { + AddCXXBaseSpecifier(*S); + } + /// Emit a reference to a type. void AddTypeRef(QualType T) { return Writer->AddTypeRef(T, *Record); @@ -239,6 +250,11 @@ class ASTRecordWriter AddDeclRef(D); } + /// Emit a TagDataMemberSpec. + void writeTagDataMemberSpecRef(const TagDataMemberSpec *Spec) { + // TODO(P2996): Implement this. + } + /// Emit a declaration name. void AddDeclarationName(DeclarationName Name) { writeDeclarationName(Name); diff --git a/clang/include/clang/Serialization/TypeBitCodes.def b/clang/include/clang/Serialization/TypeBitCodes.def index 82b053d4caca6..0e75638b060db 100644 --- a/clang/include/clang/Serialization/TypeBitCodes.def +++ b/clang/include/clang/Serialization/TypeBitCodes.def @@ -1,5 +1,7 @@ //===--- TypeNodeBitCodes.def - Type to bitcode correspondance --*- C++ -*-===// // +// Copyright 2024 Bloomberg Finance L.P. +// // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception @@ -67,5 +69,6 @@ TYPE_BIT_CODE(BTFTagAttributed, BTFTAG_ATTRIBUTED, 55) TYPE_BIT_CODE(PackIndexing, PACK_INDEXING, 56) TYPE_BIT_CODE(CountAttributed, COUNT_ATTRIBUTED, 57) TYPE_BIT_CODE(ArrayParameter, ARRAY_PARAMETER, 58) +TYPE_BIT_CODE(ReflectionSplice, REFLECTION_SPLICE, 59) #undef TYPE_BIT_CODE diff --git a/clang/lib/AST/APValue.cpp b/clang/lib/AST/APValue.cpp index d8042321319a6..927e1f0ac8902 100644 --- a/clang/lib/AST/APValue.cpp +++ b/clang/lib/AST/APValue.cpp @@ -1,5 +1,7 @@ //===--- APValue.cpp - Union class for APFloat/APSInt/Complex -------------===// // +// Copyright 2024 Bloomberg Finance L.P. +// // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception @@ -376,6 +378,11 @@ APValue::APValue(const APValue &RHS) : Kind(None) { MakeAddrLabelDiff(); setAddrLabelDiff(RHS.getAddrLabelDiffLHS(), RHS.getAddrLabelDiffRHS()); break; + case Reflection: { + const ReflectionValue& Refl = RHS.getReflection(); + MakeReflection(Refl.getKind(), Refl.getOpaqueValue()); + break; + } } } @@ -433,6 +440,7 @@ bool APValue::needsCleanup() const { case None: case Indeterminate: case AddrLabelDiff: + case Reflection: return false; case Struct: case Union: @@ -614,11 +622,56 @@ void APValue::Profile(llvm::FoldingSetNodeID &ID) const { for (const CXXRecordDecl *D : getMemberPointerPath()) ID.AddPointer(D); return; + case Reflection: + getReflection().Profile(ID); + return; } llvm_unreachable("Unknown APValue kind!"); } +QualType APValue::getReflectedType() const { + const ReflectionValue& Refl = getReflection(); + assert(Refl.getKind() == ReflectionValue::RK_type); + return Refl.getAsType(); +} + +ConstantExpr *APValue::getReflectedConstValueExpr() const { + const ReflectionValue &Refl = getReflection(); + assert(Refl.getKind() == ReflectionValue::RK_const_value); + return Refl.getAsConstValueExpr(); +} + +ValueDecl *APValue::getReflectedDecl() const { + const ReflectionValue &Refl = getReflection(); + assert(Refl.getKind() == ReflectionValue::RK_declaration); + return Refl.getAsDecl(); +} + +const TemplateName APValue::getReflectedTemplate() const { + const ReflectionValue &Refl = getReflection(); + assert(Refl.getKind() == ReflectionValue::RK_template); + return Refl.getAsTemplate(); +} + +Decl *APValue::getReflectedNamespace() const { + const ReflectionValue &Refl = getReflection(); + assert(Refl.getKind() == ReflectionValue::RK_namespace); + return Refl.getAsNamespace(); +} + +CXXBaseSpecifier *APValue::getReflectedBaseSpecifier() const { + const ReflectionValue &Refl = getReflection(); + assert(Refl.getKind() == ReflectionValue::RK_base_specifier); + return Refl.getAsBaseSpecifier(); +} + +TagDataMemberSpec *APValue::getReflectedDataMemberSpec() const { + const ReflectionValue &Refl = getReflection(); + assert(Refl.getKind() == ReflectionValue::RK_data_member_spec); + return Refl.getAsDataMemberSpec(); +} + static double GetApproxValue(const llvm::APFloat &F) { llvm::APFloat V = F; bool ignored; @@ -938,6 +991,34 @@ void APValue::printPretty(raw_ostream &Out, const PrintingPolicy &Policy, Out << " - "; Out << "&&" << getAddrLabelDiffRHS()->getLabel()->getName(); return; + case APValue::Reflection: + const ReflectionValue& Refl = getReflection(); + std::string Repr("..."); + switch (Refl.getKind()) { + case ReflectionValue::RK_type: + Repr = "type"; + break; + case ReflectionValue::RK_const_value: + Repr = "constant-value"; + break; + case ReflectionValue::RK_declaration: + Repr = "declaration"; + break; + case ReflectionValue::RK_template: + Repr = "template"; + break; + case ReflectionValue::RK_namespace: + Repr = "namespace"; + break; + case ReflectionValue::RK_base_specifier: + Repr = "base-specifier"; + break; + case ReflectionValue::RK_data_member_spec: + Repr = "data-member-spec"; + break; + } + Out << "^(" << Repr << ")"; + return; } llvm_unreachable("Unknown APValue kind!"); } @@ -1133,6 +1214,7 @@ LinkageInfo LinkageComputer::getLVForValue(const APValue &V, case APValue::ComplexInt: case APValue::ComplexFloat: case APValue::Vector: + case APValue::Reflection: break; case APValue::AddrLabelDiff: diff --git a/clang/lib/AST/ASTContext.cpp b/clang/lib/AST/ASTContext.cpp index f7f55dc4e7a9f..5078b9ace36d8 100644 --- a/clang/lib/AST/ASTContext.cpp +++ b/clang/lib/AST/ASTContext.cpp @@ -1,5 +1,7 @@ //===- ASTContext.cpp - Context to hold long-lived AST nodes --------------===// // +// Copyright 2024 Bloomberg Finance L.P. +// // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception @@ -34,6 +36,7 @@ #include "clang/AST/ExprCXX.h" #include "clang/AST/ExprConcepts.h" #include "clang/AST/ExternalASTSource.h" +#include "clang/AST/LocInfoType.h" #include "clang/AST/Mangle.h" #include "clang/AST/MangleNumberingContext.h" #include "clang/AST/NestedNameSpecifier.h" @@ -1403,6 +1406,9 @@ void ASTContext::InitBuiltinTypes(const TargetInfo &Target, // nullptr type (C++0x 2.14.7) InitBuiltinType(NullPtrTy, BuiltinType::NullPtr); + // meta::info type (C++2c P2996) + InitBuiltinType(MetaInfoTy, BuiltinType::MetaInfo); + // half type (OpenCL 6.1.1.1) / ARM NEON __fp16 InitBuiltinType(HalfTy, BuiltinType::Half); @@ -2136,6 +2142,10 @@ TypeInfo ASTContext::getTypeInfoImpl(const Type *T) const { Width = Target->getPointerWidth(LangAS::Default); Align = Target->getPointerAlign(LangAS::Default); break; + case BuiltinType::MetaInfo: + Width = Target->getMetaInfoWidth(); + Align = Target->getMetaInfoAlign(); + break; case BuiltinType::ObjCId: case BuiltinType::ObjCClass: case BuiltinType::ObjCSel: @@ -3672,6 +3682,7 @@ QualType ASTContext::getVariableArrayDecayedType(QualType type) const { case Type::Auto: case Type::DeducedTemplateSpecialization: case Type::PackExpansion: + case Type::ReflectionSplice: case Type::PackIndexing: case Type::BitInt: case Type::DependentBitInt: @@ -5315,6 +5326,40 @@ QualType ASTContext::getPackExpansionType(QualType Pattern, return QualType(T, 0); } +QualType ASTContext::getReflectionSpliceType(Expr *Operand, + QualType UnderlyingType) const { + ReflectionSpliceType *RST; + + // Unwrap any LocInfoType introduced by reflection operator. + const Type *UnderlyingTyPtr = UnderlyingType.getTypePtr(); + if (const LocInfoType *LIT = dyn_cast_or_null(UnderlyingTyPtr)) + UnderlyingType = LIT->getType(); + + if (Operand->isInstantiationDependent()) { + llvm::FoldingSetNodeID ID; + DependentReflectionSpliceType::Profile(ID, *this, Operand); + + void *InsertPos = nullptr; + DependentReflectionSpliceType *Canon = + DependentReflectionSpliceTypes.FindNodeOrInsertPos(ID, InsertPos); + if (!Canon) { // Build a new canonical splice type. + Canon = new (*this, TypeAlignment) DependentReflectionSpliceType( + *this, Operand); + DependentReflectionSpliceTypes.InsertNode(Canon, InsertPos); + } + RST = new (*this, TypeAlignment) ReflectionSpliceType(Operand, + UnderlyingType, + QualType(Canon, 0)); + } else { + CanQualType Canon = getCanonicalType(UnderlyingType); + RST = new (*this, TypeAlignment) ReflectionSpliceType(Operand, + UnderlyingType, + Canon); + } + Types.push_back(RST); + return QualType(RST, 0); +} + /// CmpProtocolNames - Comparison predicate for sorting protocols /// alphabetically. static int CmpProtocolNames(ObjCProtocolDecl *const *LHS, @@ -6555,6 +6600,11 @@ static bool isSameQualifier(const NestedNameSpecifier *X, Y->getAsType()->getCanonicalTypeInternal()) return false; break; + case NestedNameSpecifier::IndeterminateSplice: + // TODO(P2996): This might not be good enough. + if (X->getAsSpliceExpr() != Y->getAsSpliceExpr()) + return false; + break; case NestedNameSpecifier::Global: case NestedNameSpecifier::Super: return true; @@ -6866,6 +6916,10 @@ ASTContext::getCanonicalTemplateArgument(const TemplateArgument &Arg) const { case TemplateArgument::Integral: return TemplateArgument(Arg, getCanonicalType(Arg.getIntegralType())); + case TemplateArgument::Reflection: + case TemplateArgument::IndeterminateSplice: + return Arg; + case TemplateArgument::StructuralValue: return TemplateArgument(*this, getCanonicalType(Arg.getStructuralValueType()), @@ -6943,6 +6997,7 @@ ASTContext::getCanonicalNestedNameSpecifier(NestedNameSpecifier *NNS) const { case NestedNameSpecifier::Global: case NestedNameSpecifier::Super: + case NestedNameSpecifier::IndeterminateSplice: // The global specifier and __super specifer are canonical and unique. return NNS; } @@ -8142,6 +8197,7 @@ static char getObjCEncodingForPrimitiveType(const ASTContext *C, case BuiltinType::SatUShortFract: case BuiltinType::SatUFract: case BuiltinType::SatULongFract: + case BuiltinType::MetaInfo: // FIXME: potentially need @encodes for these! return ' '; @@ -9387,6 +9443,25 @@ ASTContext::getDependentTemplateName(NestedNameSpecifier *NNS, return TemplateName(QTN); } +/// Retrieve the template name that represents a dependent +/// template name such as \c [:R:] where \c R is dependent. +TemplateName +ASTContext::getDependentTemplateName(CXXIndeterminateSpliceExpr *Splice) const { + llvm::FoldingSetNodeID ID; + DependentTemplateName::Profile(ID, Splice); + + void *InsertPos = nullptr; + DependentTemplateName *QTN + = DependentTemplateNames.FindNodeOrInsertPos(ID, InsertPos); + + if (!QTN) { + QTN = new (*this, alignof(DependentTemplateName)) + DependentTemplateName(Splice); + DependentTemplateNames.InsertNode(QTN, InsertPos); + } + return TemplateName(QTN); +} + TemplateName ASTContext::getSubstTemplateTemplateParm( TemplateName Replacement, Decl *AssociatedDecl, unsigned Index, std::optional PackIndex) const { @@ -12805,6 +12880,7 @@ static QualType getCommonNonSugarTypeNode(ASTContext &Ctx, const Type *X, SUGAR_FREE_TYPE(Record) SUGAR_FREE_TYPE(SubstTemplateTypeParmPack) SUGAR_FREE_TYPE(UnresolvedUsing) + SUGAR_FREE_TYPE(ReflectionSplice) #undef SUGAR_FREE_TYPE #define NON_UNIQUE_TYPE(Class) UNEXPECTED_TYPE(Class, "non-unique") NON_UNIQUE_TYPE(TypeOfExpr) @@ -13292,6 +13368,9 @@ static QualType getCommonSugarTypeNode(ASTContext &Ctx, const Type *X, Kind = TypeOfKind::Unqualified; return Ctx.getTypeOfType(Ctx.getQualifiedType(Underlying), Kind); } + case Type::ReflectionSplice: + // TODO(P2996): Revisit this. + return QualType(); case Type::TypeOfExpr: return QualType(); diff --git a/clang/lib/AST/ASTDiagnostic.cpp b/clang/lib/AST/ASTDiagnostic.cpp index 7b0d5f9cc1a93..293b054139c00 100644 --- a/clang/lib/AST/ASTDiagnostic.cpp +++ b/clang/lib/AST/ASTDiagnostic.cpp @@ -1,5 +1,7 @@ //===--- ASTDiagnostic.cpp - Diagnostic Printing Hooks for AST Nodes ------===// // +// Copyright 2024 Bloomberg Finance L.P. +// // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception @@ -575,6 +577,8 @@ class TemplateDiff { TemplateTemplate, /// Integer difference Integer, + /// Reflection difference + Reflection, /// Declaration difference, nullptr arguments are included here Declaration, /// One argument being integer and the other being declaration @@ -590,7 +594,9 @@ class TemplateDiff { QualType ArgType; Qualifiers Qual; llvm::APSInt Val; + ReflectionValue *Refl; bool IsValidInt = false; + bool IsValidRefl = false; Expr *ArgExpr = nullptr; TemplateDecl *TD = nullptr; ValueDecl *VD = nullptr; @@ -698,6 +704,25 @@ class TemplateDiff { SetDefault(FromDefault, ToDefault); } + void SetReflectionDiff(ReflectionValue *FromRefl, + ReflectionValue *ToRefl, + bool IsValidFromRefl, bool IsValidToRefl, + QualType MetaInfoType, + Expr *FromExpr, Expr *ToExpr, bool FromDefault, + bool ToDefault) { + assert(FlatTree[CurrentNode].Kind == Invalid && "Node is not empty."); + FlatTree[CurrentNode].Kind = Reflection; + FlatTree[CurrentNode].FromArgInfo.Refl = FromRefl; + FlatTree[CurrentNode].ToArgInfo.Refl = ToRefl; + FlatTree[CurrentNode].FromArgInfo.IsValidRefl = IsValidFromRefl; + FlatTree[CurrentNode].ToArgInfo.IsValidRefl = IsValidToRefl; + FlatTree[CurrentNode].FromArgInfo.ArgType = MetaInfoType; + FlatTree[CurrentNode].ToArgInfo.ArgType = MetaInfoType; + FlatTree[CurrentNode].FromArgInfo.ArgExpr = FromExpr; + FlatTree[CurrentNode].ToArgInfo.ArgExpr = ToExpr; + SetDefault(FromDefault, ToDefault); + } + void SetDeclarationDiff(ValueDecl *FromValueDecl, ValueDecl *ToValueDecl, bool FromAddressOf, bool ToAddressOf, bool FromNullPtr, bool ToNullPtr, Expr *FromExpr, @@ -1211,6 +1236,8 @@ class TemplateDiff { NonTypeTemplateParmDecl *Default, llvm::APSInt &Value, bool &HasInt, QualType &IntType, bool &IsNullPtr, + bool &HasReflection, + ReflectionValue *&ReflValue, Expr *&E, ValueDecl *&VD, bool &NeedAddressOf) { if (!Iter.isEnd()) { @@ -1282,20 +1309,26 @@ class TemplateDiff { Expr *FromExpr = nullptr, *ToExpr = nullptr; llvm::APSInt FromInt, ToInt; QualType FromIntType, ToIntType; + ReflectionValue *FromRefl, *ToRefl; ValueDecl *FromValueDecl = nullptr, *ToValueDecl = nullptr; bool HasFromInt = false, HasToInt = false, FromNullPtr = false, - ToNullPtr = false, NeedFromAddressOf = false, NeedToAddressOf = false; + ToNullPtr = false, NeedFromAddressOf = false, NeedToAddressOf = false, + HasFromRefl = false, HasToRefl = false; InitializeNonTypeDiffVariables( Context, FromIter, FromDefaultNonTypeDecl, FromInt, HasFromInt, - FromIntType, FromNullPtr, FromExpr, FromValueDecl, NeedFromAddressOf); + FromIntType, FromNullPtr, HasFromRefl, FromRefl, FromExpr, + FromValueDecl, NeedFromAddressOf); InitializeNonTypeDiffVariables(Context, ToIter, ToDefaultNonTypeDecl, ToInt, - HasToInt, ToIntType, ToNullPtr, ToExpr, - ToValueDecl, NeedToAddressOf); + HasToInt, ToIntType, ToNullPtr, HasToRefl, + ToRefl, ToExpr, ToValueDecl, + NeedToAddressOf); bool FromDefault = FromIter.isEnd() && - (FromExpr || FromValueDecl || HasFromInt || FromNullPtr); + (FromExpr || FromValueDecl || HasFromInt || + FromNullPtr || HasFromRefl); bool ToDefault = ToIter.isEnd() && - (ToExpr || ToValueDecl || HasToInt || ToNullPtr); + (ToExpr || ToValueDecl || HasToInt || HasToRefl || + ToNullPtr); bool FromDeclaration = FromValueDecl || FromNullPtr; bool ToDeclaration = ToValueDecl || ToNullPtr; @@ -1327,6 +1360,24 @@ class TemplateDiff { return; } + if (FromDeclaration && HasToRefl) { + llvm_unreachable("unimplemented"); + } + + if (HasFromRefl && ToDeclaration) { + llvm_unreachable("unimplemented"); + } + + if (HasFromRefl || HasToRefl) { + Tree.SetReflectionDiff(FromRefl, ToRefl, HasFromRefl, HasToRefl, + Context.MetaInfoTy, FromExpr, ToExpr, + FromDefault, ToDefault); + if (HasFromRefl && HasToRefl) { + Tree.SetSame(FromRefl == ToRefl); + } + return; + } + if (FromDeclaration || ToDeclaration) { Tree.SetDeclarationDiff(FromValueDecl, ToValueDecl, NeedFromAddressOf, NeedToAddressOf, FromNullPtr, ToNullPtr, FromExpr, @@ -1536,6 +1587,10 @@ class TemplateDiff { Tree.ToDefault(), Tree.NodeIsSame()); return; } + case DiffTree::Reflection: { + llvm_unreachable("unimplemented"); + return; + } case DiffTree::Declaration: { ValueDecl *FromValueDecl, *ToValueDecl; bool FromAddressOf, ToAddressOf; diff --git a/clang/lib/AST/ASTImporter.cpp b/clang/lib/AST/ASTImporter.cpp index 45d4c9600537b..409516b270b8d 100644 --- a/clang/lib/AST/ASTImporter.cpp +++ b/clang/lib/AST/ASTImporter.cpp @@ -1,5 +1,7 @@ //===- ASTImporter.cpp - Importing ASTs from other Contexts ---------------===// // +// Copyright 2024 Bloomberg Finance L.P. +// // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception @@ -804,6 +806,10 @@ ASTNodeImporter::import(const TemplateArgument &From) { return TemplateArgument(From, *ToTypeOrErr); } + case TemplateArgument::Reflection: { + return TemplateArgument(From, Importer.getToContext().MetaInfoTy); + } + case TemplateArgument::Declaration: { Expected ToOrErr = import(From.getAsDecl()); if (!ToOrErr) @@ -1446,6 +1452,20 @@ ExpectedType ASTNodeImporter::VisitDecltypeType(const DecltypeType *T) { *ToExprOrErr, *ToUnderlyingTypeOrErr); } +ExpectedType ASTNodeImporter::VisitReflectionSpliceType( + const ReflectionSpliceType *T) { + ExpectedExpr ToExprOrErr = import(T->getOperand()); + if (!ToExprOrErr) + return ToExprOrErr.takeError(); + + ExpectedType ToUnderlyingTypeOrErr = import(T->getUnderlyingType()); + if (!ToUnderlyingTypeOrErr) + return ToUnderlyingTypeOrErr.takeError(); + + return Importer.getToContext().getReflectionSpliceType( + *ToExprOrErr, *ToUnderlyingTypeOrErr); +} + ExpectedType ASTNodeImporter::VisitUnaryTransformType(const UnaryTransformType *T) { ExpectedType ToBaseTypeOrErr = import(T->getBaseType()); @@ -2310,9 +2330,9 @@ Error ASTNodeImporter::ImportDefinition( new (Importer.getToContext()) CXXBaseSpecifier( *RangeOrErr, Base1.isVirtual(), - Base1.isBaseOfClass(), Base1.getAccessSpecifierAsWritten(), *TSIOrErr, + Base1.getDerived(), EllipsisLoc)); } if (!Bases.empty()) @@ -3616,6 +3636,8 @@ class IsTypeDeclaredInsideVisitor return false; case TemplateArgument::Integral: return CheckType(Arg.getIntegralType()); + case TemplateArgument::Reflection: + return CheckType(ParentDC->getParentASTContext().MetaInfoTy); case TemplateArgument::Type: return CheckType(Arg.getAsType()); case TemplateArgument::Expression: @@ -9677,6 +9699,9 @@ ASTImporter::Import(NestedNameSpecifier *FromNNS) { } else { return TyOrErr.takeError(); } + + case NestedNameSpecifier::IndeterminateSplice: + llvm_unreachable("unimplemented"); } llvm_unreachable("Invalid nested name specifier kind"); @@ -9761,6 +9786,17 @@ ASTImporter::Import(NestedNameSpecifierLoc FromNNS) { Builder.MakeSuper(getToContext(), Spec->getAsRecordDecl(), ToSourceRangeOrErr->getBegin(), ToSourceRangeOrErr->getEnd()); + break; + } + + case NestedNameSpecifier::IndeterminateSplice: { + auto ToSourceRangeOrErr = Import(NNS.getSourceRange()); + if (!ToSourceRangeOrErr) + return ToSourceRangeOrErr.takeError(); + + Builder.MakeIndeterminateSplice(getToContext(), Spec->getAsSpliceExpr(), + ToSourceRangeOrErr->getEnd()); + break; } } } @@ -10060,8 +10096,9 @@ ASTImporter::Import(const CXXBaseSpecifier *BaseSpec) { if (!ToEllipsisLoc) return ToEllipsisLoc.takeError(); CXXBaseSpecifier *Imported = new (ToContext) CXXBaseSpecifier( - *ToSourceRange, BaseSpec->isVirtual(), BaseSpec->isBaseOfClass(), - BaseSpec->getAccessSpecifierAsWritten(), *ToTSI, *ToEllipsisLoc); + *ToSourceRange, BaseSpec->isVirtual(), + BaseSpec->getAccessSpecifierAsWritten(), *ToTSI, BaseSpec->getDerived(), + *ToEllipsisLoc); ImportedCXXBaseSpecifiers[BaseSpec] = Imported; return Imported; } @@ -10219,6 +10256,7 @@ ASTNodeImporter::ImportAPValue(const APValue &FromValue) { case APValue::FixedPoint: case APValue::ComplexInt: case APValue::ComplexFloat: + case APValue::Reflection: Result = FromValue; break; case APValue::Vector: { diff --git a/clang/lib/AST/ASTStructuralEquivalence.cpp b/clang/lib/AST/ASTStructuralEquivalence.cpp index d56bf21b459e0..3a4dfec097b51 100644 --- a/clang/lib/AST/ASTStructuralEquivalence.cpp +++ b/clang/lib/AST/ASTStructuralEquivalence.cpp @@ -1,5 +1,7 @@ //===- ASTStructuralEquivalence.cpp ---------------------------------------===// // +// Copyright 2024 Bloomberg Finance L.P. +// // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception @@ -562,8 +564,11 @@ static bool IsStructurallyEquivalent(StructuralEquivalenceContext &Context, case NestedNameSpecifier::Global: return true; case NestedNameSpecifier::Super: - return IsStructurallyEquivalent(Context, NNS1->getAsRecordDecl(), + return IsStructurallyEquivalent(Context, + NNS1->getAsRecordDecl(), NNS2->getAsRecordDecl()); + case NestedNameSpecifier::IndeterminateSplice: + llvm::report_fatal_error("unimplemented: IsStructurallyEquivalent"); } return false; } @@ -667,6 +672,9 @@ static bool IsStructurallyEquivalent(StructuralEquivalenceContext &Context, return llvm::APSInt::isSameValue(Arg1.getAsIntegral(), Arg2.getAsIntegral()); + case TemplateArgument::Reflection: + return Arg1.getAsReflection() == Arg2.getAsReflection(); + case TemplateArgument::Declaration: return IsStructurallyEquivalent(Context, Arg1.getAsDecl(), Arg2.getAsDecl()); @@ -1143,6 +1151,13 @@ static bool IsStructurallyEquivalent(StructuralEquivalenceContext &Context, return false; break; + case Type::ReflectionSplice: + if (!IsStructurallyEquivalent(Context, + cast(T1)->getOperand(), + cast(T2)->getOperand())) + return false; + break; + case Type::Auto: { auto *Auto1 = cast(T1); auto *Auto2 = cast(T2); diff --git a/clang/lib/AST/CMakeLists.txt b/clang/lib/AST/CMakeLists.txt index 3faefb54f599f..51b61cbda2711 100644 --- a/clang/lib/AST/CMakeLists.txt +++ b/clang/lib/AST/CMakeLists.txt @@ -108,6 +108,7 @@ add_clang_library(clangAST RawCommentList.cpp RecordLayout.cpp RecordLayoutBuilder.cpp + Reflection.cpp ScanfFormatString.cpp SelectorLocationsKind.cpp Stmt.cpp diff --git a/clang/lib/AST/ComputeDependence.cpp b/clang/lib/AST/ComputeDependence.cpp index 86b77b49a0fbc..fb7259d806869 100644 --- a/clang/lib/AST/ComputeDependence.cpp +++ b/clang/lib/AST/ComputeDependence.cpp @@ -1,5 +1,7 @@ //===- ComputeDependence.cpp ----------------------------------------------===// // +// Copyright 2024 Bloomberg Finance L.P. +// // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception @@ -22,7 +24,9 @@ using namespace clang; ExprDependence clang::computeDependence(FullExpr *E) { - return E->getSubExpr()->getDependence(); + if (Expr *Sub = E->getSubExpr()) + return Sub->getDependence(); + return ExprDependence::None; } ExprDependence clang::computeDependence(OpaqueValueExpr *E) { @@ -470,57 +474,17 @@ ExprDependence clang::computeDependence(OMPIteratorExpr *E) { return D; } -/// Compute the type-, value-, and instantiation-dependence of a -/// declaration reference -/// based on the declaration being referenced. -ExprDependence clang::computeDependence(DeclRefExpr *E, const ASTContext &Ctx) { +static ExprDependence computeDeclDependence(ValueDecl *D, + const ASTContext &Ctx) { auto Deps = ExprDependence::None; - if (auto *NNS = E->getQualifier()) - Deps |= toExprDependence(NNS->getDependence() & - ~NestedNameSpecifierDependence::Dependent); - - if (auto *FirstArg = E->getTemplateArgs()) { - unsigned NumArgs = E->getNumTemplateArgs(); - for (auto *Arg = FirstArg, *End = FirstArg + NumArgs; Arg < End; ++Arg) - Deps |= toExprDependence(Arg->getArgument().getDependence()); - } - - auto *Decl = E->getDecl(); - auto Type = E->getType(); - - if (Decl->isParameterPack()) + if (D->isParameterPack()) Deps |= ExprDependence::UnexpandedPack; - Deps |= toExprDependenceForImpliedType(Type->getDependence()) & - ExprDependence::Error; - - // C++ [temp.dep.expr]p3: - // An id-expression is type-dependent if it contains: - - // - an identifier associated by name lookup with one or more declarations - // declared with a dependent type - // - an identifier associated by name lookup with an entity captured by - // copy ([expr.prim.lambda.capture]) - // in a lambda-expression that has an explicit object parameter whose - // type is dependent ([dcl.fct]), - // - // [The "or more" case is not modeled as a DeclRefExpr. There are a bunch - // more bullets here that we handle by treating the declaration as having a - // dependent type if they involve a placeholder type that can't be deduced.] - if (Type->isDependentType()) - Deps |= ExprDependence::TypeValueInstantiation; - else if (Type->isInstantiationDependentType()) - Deps |= ExprDependence::Instantiation; - - // - an identifier associated by name lookup with an entity captured by - // copy ([expr.prim.lambda.capture]) - if (E->isCapturedByCopyInLambdaWithExplicitObjectParameter()) - Deps |= ExprDependence::Type; // - a conversion-function-id that specifies a dependent type - if (Decl->getDeclName().getNameKind() == + if (D->getDeclName().getNameKind() == DeclarationName::CXXConversionFunctionName) { - QualType T = Decl->getDeclName().getCXXNameType(); + QualType T = D->getDeclName().getCXXNameType(); if (T->isDependentType()) return Deps | ExprDependence::TypeValueInstantiation; @@ -543,12 +507,12 @@ ExprDependence clang::computeDependence(DeclRefExpr *E, const ASTContext &Ctx) { // - it is type-dependent [handled above] // - it is the name of a non-type template parameter, - if (isa(Decl)) + if (isa(D)) return Deps | ExprDependence::ValueInstantiation; // - it names a potentially-constant variable that is initialized with an // expression that is value-dependent - if (const auto *Var = dyn_cast(Decl)) { + if (const auto *Var = dyn_cast(D)) { if (const Expr *Init = Var->getAnyInitializer()) { if (Init->containsErrors()) Deps |= ExprDependence::Error; @@ -582,14 +546,59 @@ ExprDependence clang::computeDependence(DeclRefExpr *E, const ASTContext &Ctx) { // effect: any use of a non-static member function name requires either // forming a pointer-to-member or providing an object parameter, either of // which makes the overall expression value-dependent. - if (auto *MD = dyn_cast(Decl)) { - if (MD->isStatic() && Decl->getDeclContext()->isDependentContext()) + if (auto *MD = dyn_cast(D)) { + if (MD->isStatic() && D->getDeclContext()->isDependentContext()) Deps |= ExprDependence::ValueInstantiation; } return Deps; } +/// Compute the type-, value-, and instantiation-dependence of a +/// declaration reference +/// based on the declaration being referenced. +ExprDependence clang::computeDependence(DeclRefExpr *E, const ASTContext &Ctx) { + auto Deps = ExprDependence::None; + + if (auto *NNS = E->getQualifier()) + Deps |= toExprDependence(NNS->getDependence() & + ~NestedNameSpecifierDependence::Dependent); + + if (auto *FirstArg = E->getTemplateArgs()) { + unsigned NumArgs = E->getNumTemplateArgs(); + for (auto *Arg = FirstArg, *End = FirstArg + NumArgs; Arg < End; ++Arg) + Deps |= toExprDependence(Arg->getArgument().getDependence()); + } + + auto Type = E->getType(); + + // C++ [temp.dep.expr]p3: + // An id-expression is type-dependent if it contains: + + // - an identifier associated by name lookup with one or more declarations + // declared with a dependent type + // - an identifier associated by name lookup with an entity captured by + // copy ([expr.prim.lambda.capture]) + // in a lambda-expression that has an explicit object parameter whose + // type is dependent ([dcl.fct]), + // + // [The "or more" case is not modeled as a DeclRefExpr. There are a bunch + // more bullets here that we handle by treating the declaration as having a + // dependent type if they involve a placeholder type that can't be deduced.] + if (Type->isDependentType()) + Deps |= ExprDependence::TypeValueInstantiation; + else if (Type->isInstantiationDependentType()) + Deps |= ExprDependence::Instantiation; + + Deps |= toExprDependenceForImpliedType(Type->getDependence()) & + ExprDependence::Error; + + + Deps |= computeDeclDependence(E->getDecl(), Ctx); + + return Deps; +} + ExprDependence clang::computeDependence(RecoveryExpr *E) { // RecoveryExpr is // - always value-dependent, and therefore instantiation dependent @@ -905,6 +914,81 @@ ExprDependence clang::computeDependence(ConceptSpecializationExpr *E, return Res; } +ExprDependence clang::computeDependence(CXXReflectExpr *E, + const ASTContext &Ctx) { + const ReflectionValue &Operand = E->getOperand(); + + ExprDependence D = ExprDependence::None; + switch (Operand.getKind()) { + case ReflectionValue::RK_type: { + QualType T = Operand.getAsType(); + + if (T->isDependentType()) + D |= ExprDependence::ValueInstantiation; + if (T->containsUnexpandedParameterPack()) + D |= ExprDependence::UnexpandedPack; + return D; + } + case ReflectionValue::RK_const_value: + return Operand.getAsConstValueExpr()->getDependence(); + case ReflectionValue::RK_declaration: + return computeDeclDependence(Operand.getAsDecl(), Ctx); + case ReflectionValue::RK_namespace: + case ReflectionValue::RK_base_specifier: + case ReflectionValue::RK_data_member_spec: + return ExprDependence::None; + case ReflectionValue::RK_template: { + const TemplateName Template = Operand.getAsTemplate(); + + if (Template.isDependent()) + D |= ExprDependence::ValueInstantiation; + if (Template.isInstantiationDependent()) + D |= ExprDependence::Instantiation; + if (Template.containsUnexpandedParameterPack()) + D |= ExprDependence::UnexpandedPack; + return D; + } + } + llvm_unreachable("unknown reflection kind while computing dependence"); +} + +ExprDependence clang::computeDependence(CXXMetafunctionExpr *E) { + auto D = ExprDependence::None; + for (unsigned I = 0; I < E->getNumArgs(); ++I) { + Expr *Arg = E->getArg(I); + D |= Arg->getDependence(); + } + return D & ~ExprDependence::UnexpandedPack; +} + + +ExprDependence clang::computeDependence(CXXIndeterminateSpliceExpr *E) { + return E->getOperand()->getDependence(); +} + +ExprDependence clang::computeDependence(CXXExprSpliceExpr *E) { + auto D = E->getOperand()->getDependence(); + if (D & ExprDependence::Value) + D |= ExprDependence::Type; + return D; +} + +ExprDependence clang::computeDependence(CXXDependentMemberSpliceExpr *E) { + auto D = E->getBase()->getDependence() | E->getRHS()->getDependence(); + if (D & ExprDependence::Value) + D |= ExprDependence::Type; + return D; +} + +ExprDependence clang::computeDependence(StackLocationExpr *E) { + return ExprDependence::None; +} + +ExprDependence clang::computeDependence(ValueOfLValueExpr *E) { + return ExprDependence::None; +} + + ExprDependence clang::computeDependence(ObjCArrayLiteral *E) { auto D = ExprDependence::None; Expr **Elements = E->getElements(); diff --git a/clang/lib/AST/Decl.cpp b/clang/lib/AST/Decl.cpp index 131f82985e903..ea416a012c6c6 100644 --- a/clang/lib/AST/Decl.cpp +++ b/clang/lib/AST/Decl.cpp @@ -325,6 +325,7 @@ LinkageComputer::getLVForTemplateArgumentList(ArrayRef Args, switch (Arg.getKind()) { case TemplateArgument::Null: case TemplateArgument::Integral: + case TemplateArgument::Reflection: case TemplateArgument::Expression: continue; @@ -4976,6 +4977,7 @@ RecordDecl::RecordDecl(Kind DK, TagKind TK, const ASTContext &C, setParamDestroyedInCallee(false); setArgPassingRestrictions(RecordArgPassingKind::CanPassInRegs); setIsRandomized(false); + setIsMetaType(false); setODRHash(0); } @@ -5050,6 +5052,22 @@ void RecordDecl::completeDefinition() { ASTContext &Ctx = getASTContext(); + // Compute whether this is a meta type. + for (FieldDecl *FD : fields()) { + if (FD->getType()->isMetaType()) { + setIsMetaType(true); + break; + } + } + if (auto CXXRD = dyn_cast(this); CXXRD && !isMetaType()) { + for (CXXBaseSpecifier BaseSpecifier : CXXRD->bases()) { + if (BaseSpecifier.getType()->isMetaType()) { + setIsMetaType(true); + break; + } + } + } + // Layouts are dumped when computed, so if we are dumping for all complete // types, we need to force usage to get types that wouldn't be used elsewhere. // diff --git a/clang/lib/AST/DeclBase.cpp b/clang/lib/AST/DeclBase.cpp index 66a727d9dd0c3..6c204730bdbdb 100644 --- a/clang/lib/AST/DeclBase.cpp +++ b/clang/lib/AST/DeclBase.cpp @@ -1,5 +1,7 @@ //===- DeclBase.cpp - Declaration AST Node Implementation -----------------===// // +// Copyright 2024 Bloomberg Finance L.P. +// // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception @@ -890,6 +892,7 @@ unsigned Decl::getIdentifierNamespaceForKind(Kind DeclKind) { case Namespace: case NamespaceAlias: + case DependentNamespace: return IDNS_Namespace; case FunctionTemplate: diff --git a/clang/lib/AST/DeclCXX.cpp b/clang/lib/AST/DeclCXX.cpp index 645ec2f7563bc..50f5be79fe436 100644 --- a/clang/lib/AST/DeclCXX.cpp +++ b/clang/lib/AST/DeclCXX.cpp @@ -2959,11 +2959,11 @@ NamespaceDecl *UsingDirectiveDecl::getNominatedNamespace() { return cast_or_null(NominatedNamespace); } -NamespaceDecl::NamespaceDecl(ASTContext &C, DeclContext *DC, bool Inline, - SourceLocation StartLoc, SourceLocation IdLoc, - IdentifierInfo *Id, NamespaceDecl *PrevDecl, - bool Nested) - : NamedDecl(Namespace, DC, IdLoc, Id), DeclContext(Namespace), +NamespaceDecl::NamespaceDecl(Kind K, ASTContext &C, DeclContext *DC, + bool Inline, SourceLocation StartLoc, + SourceLocation IdLoc, IdentifierInfo *Id, + NamespaceDecl *PrevDecl, bool Nested) + : NamedDecl(K, DC, IdLoc, Id), DeclContext(Namespace), redeclarable_base(C), LocStart(StartLoc) { unsigned Flags = 0; if (Inline) @@ -2982,12 +2982,14 @@ NamespaceDecl *NamespaceDecl::Create(ASTContext &C, DeclContext *DC, SourceLocation IdLoc, IdentifierInfo *Id, NamespaceDecl *PrevDecl, bool Nested) { return new (C, DC) - NamespaceDecl(C, DC, Inline, StartLoc, IdLoc, Id, PrevDecl, Nested); + NamespaceDecl(Namespace, C, DC, Inline, StartLoc, IdLoc, Id, PrevDecl, + Nested); } NamespaceDecl *NamespaceDecl::CreateDeserialized(ASTContext &C, unsigned ID) { - return new (C, ID) NamespaceDecl(C, nullptr, false, SourceLocation(), - SourceLocation(), nullptr, nullptr, false); + return new (C, ID) NamespaceDecl(Namespace, C, nullptr, false, + SourceLocation(), SourceLocation(), nullptr, + nullptr, false); } NamespaceDecl *NamespaceDecl::getOriginalNamespace() { @@ -3054,6 +3056,28 @@ NamespaceAliasDecl::CreateDeserialized(ASTContext &C, unsigned ID) { SourceLocation(), nullptr); } +void DependentNamespaceDecl::anchor() {} + +DependentNamespaceDecl::DependentNamespaceDecl( + ASTContext &C, DeclContext *DC, CXXIndeterminateSpliceExpr *SpliceExpr) + : NamespaceDecl(DependentNamespace, C, DC, false, SpliceExpr->getBeginLoc(), + SpliceExpr->getBeginLoc(), nullptr, nullptr, false), + SpliceExpr(SpliceExpr) {} + +DependentNamespaceDecl *DependentNamespaceDecl::Create( + ASTContext &C, DeclContext *DC, CXXIndeterminateSpliceExpr *SpliceExpr) { + return new (C, DC) DependentNamespaceDecl(C, DC, SpliceExpr); +} + +DependentNamespaceDecl * +DependentNamespaceDecl::CreateDeserialized(ASTContext &C, unsigned ID) { + return new (C, ID) DependentNamespaceDecl(C, nullptr, nullptr); +} + +SourceRange DependentNamespaceDecl::getSourceRange() const { + return SourceRange(SpliceExpr->getBeginLoc(), SpliceExpr->getEndLoc()); +} + void LifetimeExtendedTemporaryDecl::anchor() {} /// Retrieve the storage duration for the materialized temporary. diff --git a/clang/lib/AST/Expr.cpp b/clang/lib/AST/Expr.cpp index 07c9f287dd076..5acb6fb349a02 100644 --- a/clang/lib/AST/Expr.cpp +++ b/clang/lib/AST/Expr.cpp @@ -1,5 +1,7 @@ //===--- Expr.cpp - Expression AST Node Implementation --------------------===// // +// Copyright 2024 Bloomberg Finance L.P. +// // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception @@ -1968,7 +1970,8 @@ static Expr *ignoreImplicitSemaNodes(Expr *E) { return Binder->getSubExpr(); if (auto *Full = dyn_cast(E)) - return Full->getSubExpr(); + if (auto *SubExpr = Full->getSubExpr()) + return SubExpr; if (auto *CPLIE = dyn_cast(E); CPLIE && CPLIE->getInitExprs().size() == 1) @@ -2245,7 +2248,8 @@ SourceLocExpr::SourceLocExpr(const ASTContext &Ctx, SourceLocIdentKind Kind, BuiltinLoc(BLoc), RParenLoc(RParenLoc), ParentContext(ParentContext) { SourceLocExprBits.Kind = llvm::to_underlying(Kind); // In dependent contexts, function names may change. - setDependence(MayBeDependent(Kind) && ParentContext->isDependentContext() + setDependence(MayBeDependent(Kind) && ParentContext && + ParentContext->isDependentContext() ? ExprDependence::Value : ExprDependence::None); } @@ -2341,24 +2345,28 @@ APValue SourceLocExpr::EvaluateInContext(const ASTContext &Ctx, for (const FieldDecl *F : ImplDecl->fields()) { StringRef Name = F->getName(); if (Name == "_M_file_name") { - SmallString<256> Path(PLoc.getFilename()); + SmallString<256> Path(PLoc.isValid() ? PLoc.getFilename() : ""); clang::Preprocessor::processPathForFileMacro(Path, Ctx.getLangOpts(), Ctx.getTargetInfo()); Value.getStructField(F->getFieldIndex()) = MakeStringLiteral(Path); } else if (Name == "_M_function_name") { // Note: this emits the PrettyFunction name -- different than what // __builtin_FUNCTION() above returns! - const auto *CurDecl = dyn_cast(Context); + const auto *CurDecl = dyn_cast_if_present(Context); Value.getStructField(F->getFieldIndex()) = MakeStringLiteral( CurDecl && !isa(CurDecl) ? StringRef(PredefinedExpr::ComputeName( PredefinedIdentKind::PrettyFunction, CurDecl)) : ""); } else if (Name == "_M_line") { - llvm::APSInt IntVal = Ctx.MakeIntValue(PLoc.getLine(), F->getType()); + llvm::APSInt IntVal = + Ctx.MakeIntValue(PLoc.isValid() ? PLoc.getLine() : 0, + F->getType()); Value.getStructField(F->getFieldIndex()) = APValue(IntVal); } else if (Name == "_M_column") { - llvm::APSInt IntVal = Ctx.MakeIntValue(PLoc.getColumn(), F->getType()); + llvm::APSInt IntVal = + Ctx.MakeIntValue(PLoc.isValid() ? PLoc.getColumn() : 0, + F->getType()); Value.getStructField(F->getFieldIndex()) = APValue(IntVal); } } @@ -3065,6 +3073,10 @@ Expr *Expr::IgnoreParenCasts() { return IgnoreExprNodes(this, IgnoreParensSingleStep, IgnoreCastsSingleStep); } +Expr *Expr::IgnoreExprSplices() { + return IgnoreExprNodes(this, IgnoreExprSpliceSingleStep); +} + Expr *Expr::IgnoreConversionOperatorSingleStep() { if (auto *MCE = dyn_cast(this)) { if (MCE->getMethodDecl() && isa(MCE->getMethodDecl())) @@ -3339,7 +3351,7 @@ bool Expr::isConstantInitializer(ASTContext &Ctx, bool IsForRef, // FIXME: We should be able to return "true" here, but it can lead to extra // error messages. E.g. in Sema/array-init.c. const Expr *Exp = cast(this)->getSubExpr(); - return Exp->isConstantInitializer(Ctx, false, Culprit); + return Exp ? Exp->isConstantInitializer(Ctx, false, Culprit) : true; } case CompoundLiteralExprClass: { // This handles gcc's extension that allows global initializers like @@ -3617,6 +3629,13 @@ bool Expr::HasSideEffects(const ASTContext &Ctx, case SourceLocExprClass: case ConceptSpecializationExprClass: case RequiresExprClass: + case CXXReflectExprClass: + case CXXMetafunctionExprClass: + case CXXIndeterminateSpliceExprClass: + case CXXExprSpliceExprClass: + case CXXDependentMemberSpliceExprClass: + case StackLocationExprClass: + case ValueOfLValueExprClass: case SYCLUniqueStableNameExprClass: // These never have a side-effect. return false; @@ -3624,10 +3643,12 @@ bool Expr::HasSideEffects(const ASTContext &Ctx, case PackIndexingExprClass: return cast(this)->getSelectedExpr()->HasSideEffects( Ctx, IncludePossibleEffects); - case ConstantExprClass: + + case ConstantExprClass: { // FIXME: Move this into the "return false;" block above. - return cast(this)->getSubExpr()->HasSideEffects( - Ctx, IncludePossibleEffects); + const Expr *Exp = cast(this)->getSubExpr(); + return Exp ? Exp->HasSideEffects(Ctx, IncludePossibleEffects) : false; + } case CallExprClass: case CXXOperatorCallExprClass: diff --git a/clang/lib/AST/ExprCXX.cpp b/clang/lib/AST/ExprCXX.cpp index a581963188433..27ab7f0d0d13b 100644 --- a/clang/lib/AST/ExprCXX.cpp +++ b/clang/lib/AST/ExprCXX.cpp @@ -1,5 +1,7 @@ //===- ExprCXX.cpp - (C++) Expression AST Node Implementation -------------===// // +// Copyright 2024 Bloomberg Finance L.P. +// // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception @@ -1841,6 +1843,236 @@ TypeTraitExpr *TypeTraitExpr::CreateDeserialized(const ASTContext &C, return new (Mem) TypeTraitExpr(EmptyShell()); } +CXXReflectExpr::CXXReflectExpr(const ASTContext &C, QualType T, + QualType Operand) + : Expr(CXXReflectExprClass, T, VK_PRValue, OK_Ordinary), + Ref(ReflectionValue::RK_type, Operand.getAsOpaquePtr()) { + setDependence(computeDependence(this, C)); +} + +CXXReflectExpr::CXXReflectExpr(const ASTContext &C, QualType T, + Expr *Operand) + : Expr(CXXReflectExprClass, T, VK_PRValue, OK_Ordinary), + Ref(ReflectionValue::RK_const_value, Operand) { + setDependence(computeDependence(this, C)); +} + +CXXReflectExpr::CXXReflectExpr(const ASTContext &C, QualType T, Decl *Operand, + bool IsNamespace) + : Expr(CXXReflectExprClass, T, VK_PRValue, OK_Ordinary), + Ref(IsNamespace ? ReflectionValue::RK_namespace : + ReflectionValue:: RK_declaration, Operand) { + setDependence(computeDependence(this, C)); +} + +CXXReflectExpr::CXXReflectExpr(const ASTContext &C, QualType T, + const TemplateName Operand) + : Expr(CXXReflectExprClass, T, VK_PRValue, OK_Ordinary), + Ref(ReflectionValue::RK_template, Operand.getAsVoidPointer()) { + setDependence(computeDependence(this, C)); +} + +CXXReflectExpr::CXXReflectExpr(const ASTContext &C, QualType T, + CXXBaseSpecifier *Operand) + : Expr(CXXReflectExprClass, T, VK_PRValue, OK_Ordinary), + Ref(ReflectionValue::RK_base_specifier, Operand) { + setDependence(computeDependence(this, C)); +} + +CXXReflectExpr::CXXReflectExpr(const ASTContext &C, QualType T, + TagDataMemberSpec *Operand) + : Expr(CXXReflectExprClass, T, VK_PRValue, OK_Ordinary), + Ref(ReflectionValue::RK_data_member_spec, Operand) { + setDependence(computeDependence(this, C)); +} + +CXXReflectExpr *CXXReflectExpr::Create(ASTContext &C, + SourceLocation OperatorLoc, + SourceLocation OperandLoc, + QualType Operand) { + CXXReflectExpr *E = new (C) CXXReflectExpr(C, C.MetaInfoTy, Operand); + E->setOperatorLoc(OperatorLoc); + E->setArgLoc(OperandLoc); + return E; +} + +CXXReflectExpr *CXXReflectExpr::Create(ASTContext &C, + SourceLocation OperatorLoc, + Expr *Operand) { + CXXReflectExpr *E = new (C) CXXReflectExpr(C, C.MetaInfoTy, Operand); + E->setOperatorLoc(OperatorLoc); + E->setArgLoc(Operand->getExprLoc()); + return E; +} + +CXXReflectExpr *CXXReflectExpr::Create(ASTContext &C, + SourceLocation OperatorLoc, + SourceLocation OperandLoc, + Decl *Operand) { + bool IsNamespace = isa(Operand) || + isa(Operand) || + isa(Operand); + + CXXReflectExpr *E = new (C) CXXReflectExpr(C, C.MetaInfoTy, Operand, + IsNamespace); + E->setOperatorLoc(OperatorLoc); + E->setArgLoc(OperandLoc); + return E; +} + +CXXReflectExpr *CXXReflectExpr::Create(ASTContext &C, + SourceLocation OperatorLoc, + SourceLocation OperandLoc, + const TemplateName Operand) { + CXXReflectExpr *E = new (C) CXXReflectExpr(C, C.MetaInfoTy, Operand); + E->setOperatorLoc(OperatorLoc); + E->setArgLoc(OperandLoc); + return E; +} + +CXXReflectExpr *CXXReflectExpr::Create(ASTContext &C, + SourceLocation OperatorLoc, + SourceLocation OperandLoc, + CXXBaseSpecifier *Operand) { + CXXReflectExpr *E = new (C) CXXReflectExpr(C, C.MetaInfoTy, Operand); + E->setOperatorLoc(OperatorLoc); + E->setArgLoc(OperandLoc); + return E; +} + +CXXReflectExpr *CXXReflectExpr::Create(ASTContext &C, + SourceLocation OperatorLoc, + SourceLocation OperandLoc, + TagDataMemberSpec *Operand) { + CXXReflectExpr *E = new (C) CXXReflectExpr(C, C.MetaInfoTy, Operand); + E->setOperatorLoc(OperatorLoc); + E->setArgLoc(OperandLoc); + return E; +} + +static QualType UnwrapCXXMetafunctionExprReturnType(QualType QT) { + if (auto *LVRT = dyn_cast(QT)) + QT = LVRT->getPointeeType(); + + return QT; +} + +CXXMetafunctionExpr::CXXMetafunctionExpr(unsigned MetaFnID, + const ImplFn &Impl, + QualType ResultType, + ExprValueKind VK, + Expr ** Args, unsigned NumArgs, + SourceLocation KwLoc, + SourceLocation LParenLoc, + SourceLocation RParenLoc) + : Expr(CXXMetafunctionExprClass, + UnwrapCXXMetafunctionExprReturnType(ResultType), VK, OK_Ordinary), + MetaFnID(MetaFnID), Impl(Impl), ResultType(ResultType), + NumArgs(NumArgs), Args(Args), KwLoc(KwLoc), LParenLoc(LParenLoc), + RParenLoc(RParenLoc) { + std::copy(Args, Args + NumArgs, this->Args); + setDependence(computeDependence(this)); +} + +CXXMetafunctionExpr *CXXMetafunctionExpr::Create(ASTContext &C, + unsigned MetaFnID, + const ImplFn &Impl, + QualType ResultType, + ArrayRef Args, + SourceLocation KwLoc, + SourceLocation LParenLoc, + SourceLocation RParenLoc) { + Expr **args = new (C) Expr *[Args.size()]; + std::copy(Args.begin(), Args.end(), args); + + ExprValueKind VK = isa(ResultType) ? VK_LValue : + VK_PRValue; + return new (C) CXXMetafunctionExpr(MetaFnID, Impl, ResultType, VK, args, + Args.size(), KwLoc, LParenLoc, RParenLoc); +} + +CXXIndeterminateSpliceExpr::CXXIndeterminateSpliceExpr( + QualType ResultTy, SourceLocation LSpliceLoc, Expr *Operand, + SourceLocation RSpliceLoc) + : Expr(CXXIndeterminateSpliceExprClass, ResultTy, VK_PRValue, OK_Ordinary), + LSpliceLoc(LSpliceLoc), Operand(Operand), RSpliceLoc(RSpliceLoc) { + setDependence(computeDependence(this)); +} + +CXXIndeterminateSpliceExpr *CXXIndeterminateSpliceExpr::Create( + ASTContext &C, SourceLocation LSpliceLoc, Expr *Operand, + SourceLocation RSpliceLoc) { + return new (C) CXXIndeterminateSpliceExpr(C.MetaInfoTy, LSpliceLoc, Operand, + RSpliceLoc); +} + +CXXExprSpliceExpr::CXXExprSpliceExpr(QualType ResultTy, ExprValueKind ValueKind, + SourceLocation LSpliceLoc, Expr *Operand, + SourceLocation RSpliceLoc, + bool AllowMemberReference) + : Expr(CXXExprSpliceExprClass, ResultTy, ValueKind, OK_Ordinary), + LSpliceLoc(LSpliceLoc), Operand(Operand), RSpliceLoc(RSpliceLoc), + AllowMemberReference(AllowMemberReference) { + setDependence(computeDependence(this)); +} + +CXXExprSpliceExpr *CXXExprSpliceExpr::Create(ASTContext &C, + ExprValueKind ValueKind, + SourceLocation LSpliceLoc, + Expr *Operand, + SourceLocation RSpliceLoc, + bool AllowMemberReference) { + QualType ResultTy = Operand->getType(); + if (Operand->isTypeDependent() || Operand->isValueDependent()) + ResultTy = C.DependentTy; + + return new (C) CXXExprSpliceExpr(ResultTy, ValueKind, LSpliceLoc, Operand, + RSpliceLoc, AllowMemberReference); +} + +StackLocationExpr::StackLocationExpr(QualType ResultTy, SourceRange Range, + int FrameOffset) + : Expr(StackLocationExprClass, ResultTy, VK_PRValue, OK_Ordinary), + Range(Range), FrameOffset(FrameOffset) { + setDependence(computeDependence(this)); +} + +StackLocationExpr *StackLocationExpr::Create(ASTContext &C, SourceRange Range, + int FrameOffset) { + return new (C) StackLocationExpr(C.MetaInfoTy, Range, FrameOffset); +} + +ValueOfLValueExpr::ValueOfLValueExpr(QualType ResultTy, SourceRange Range, + ValueDecl *Decl) + : Expr(ValueOfLValueExprClass, ResultTy, VK_LValue, OK_Ordinary), + Range(Range), Decl(Decl) { + setDependence(computeDependence(this)); +} + +ValueOfLValueExpr *ValueOfLValueExpr::Create(ASTContext &C, SourceRange Range, + QualType ResultTy, + ValueDecl *Decl) { + return new (C) ValueOfLValueExpr(ResultTy, Range, Decl); +} + +CXXDependentMemberSpliceExpr::CXXDependentMemberSpliceExpr( + QualType ResultTy, Expr *Base, SourceLocation OpLoc, bool IsArrow, + CXXExprSpliceExpr *RHS) + : Expr(CXXDependentMemberSpliceExprClass, ResultTy, VK_LValue, OK_Ordinary), + OpLoc(OpLoc), IsArrow(IsArrow) { + SubExprs[0] = Base; + SubExprs[1] = RHS; + + setDependence(computeDependence(this)); +} + +CXXDependentMemberSpliceExpr *CXXDependentMemberSpliceExpr::Create( + ASTContext &C, Expr *Base, SourceLocation OpLoc, bool IsArrow, + CXXExprSpliceExpr *RHS) { + return new (C) CXXDependentMemberSpliceExpr(C.DependentTy, Base, OpLoc, + IsArrow, RHS); +} + CUDAKernelCallExpr::CUDAKernelCallExpr(Expr *Fn, CallExpr *Config, ArrayRef Args, QualType Ty, ExprValueKind VK, SourceLocation RP, diff --git a/clang/lib/AST/ExprClassification.cpp b/clang/lib/AST/ExprClassification.cpp index 7026fca8554ce..006e2f7bcab45 100644 --- a/clang/lib/AST/ExprClassification.cpp +++ b/clang/lib/AST/ExprClassification.cpp @@ -1,5 +1,7 @@ //===- ExprClassification.cpp - Expression AST Node Implementation --------===// // +// Copyright 2024 Bloomberg Finance L.P. +// // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception @@ -139,6 +141,7 @@ static Cl::Kinds ClassifyInternal(ASTContext &Ctx, const Expr *E) { case Expr::DependentCoawaitExprClass: case Expr::CXXDependentScopeMemberExprClass: case Expr::DependentScopeDeclRefExprClass: + case Expr::CXXDependentMemberSpliceExprClass: // ObjC instance variables are lvalues // FIXME: ObjC++0x might have different rules case Expr::ObjCIvarRefExprClass: @@ -148,6 +151,7 @@ static Cl::Kinds ClassifyInternal(ASTContext &Ctx, const Expr *E) { case Expr::OMPArraySectionExprClass: case Expr::OMPArrayShapingExprClass: case Expr::OMPIteratorExprClass: + case Expr::ValueOfLValueExprClass: return Cl::CL_LValue; // C99 6.5.2.5p5 says that compound literals are lvalues. @@ -202,14 +206,23 @@ static Cl::Kinds ClassifyInternal(ASTContext &Ctx, const Expr *E) { case Expr::SourceLocExprClass: case Expr::ConceptSpecializationExprClass: case Expr::RequiresExprClass: + case Expr::CXXReflectExprClass: + case Expr::CXXIndeterminateSpliceExprClass: + case Expr::StackLocationExprClass: return Cl::CL_PRValue; + case Expr::CXXMetafunctionExprClass: + return E->getValueKind() == VK_LValue ? Cl::CL_LValue : Cl::CL_PRValue; + // Make HLSL this reference-like case Expr::CXXThisExprClass: return Lang.HLSL ? Cl::CL_LValue : Cl::CL_PRValue; - case Expr::ConstantExprClass: + case Expr::ConstantExprClass: { + if (!cast(E)->getSubExpr()) + return Cl::CL_PRValue; return ClassifyInternal(Ctx, cast(E)->getSubExpr()); + } // Next come the complicated cases. case Expr::SubstNonTypeTemplateParmExprClass: @@ -235,6 +248,19 @@ static Cl::Kinds ClassifyInternal(ASTContext &Ctx, const Expr *E) { } return Cl::CL_LValue; + case Expr::CXXExprSpliceExprClass: { + const auto *ESE = dyn_cast(E); + if (const auto *DRE = dyn_cast(ESE->getOperand())) { + if (auto *MD = dyn_cast(DRE->getDecl()); + MD && !MD->isStatic()) + return Cl::CL_MemberFunction; + else if (isa(DRE->getDecl())) + return Cl::CL_PRValue; + return Cl::CL_LValue; + } + return Cl::CL_PRValue; + } + // Subscripting matrix types behaves like member accesses. case Expr::MatrixSubscriptExprClass: return ClassifyInternal(Ctx, cast(E)->getBase()); diff --git a/clang/lib/AST/ExprConstant.cpp b/clang/lib/AST/ExprConstant.cpp index 88c8eaf6ef9b6..aeafd8741171f 100644 --- a/clang/lib/AST/ExprConstant.cpp +++ b/clang/lib/AST/ExprConstant.cpp @@ -1,5 +1,7 @@ //===--- ExprConstant.cpp - Expression Constant Evaluator -----------------===// // +// Copyright 2024 Bloomberg Finance L.P. +// // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception @@ -2610,6 +2612,7 @@ static bool HandleConversionToBool(const APValue &Val, bool &Result) { case APValue::Struct: case APValue::Union: case APValue::AddrLabelDiff: + case APValue::Reflection: return false; } @@ -4385,7 +4388,6 @@ handleLValueToRValueConversion(EvalInfo &Info, const Expr *Conv, QualType Type, return true; } } - CompleteObject Obj = findCompleteObject(Info, Conv, AK, LVal, Type); return Obj && extractSubobject(Info, Conv, Obj, LVal.Designator, RVal, AK); } @@ -7056,6 +7058,7 @@ class APValueToBufferConverter { case APValue::FixedPoint: // FIXME: We should support these. + case APValue::Reflection: case APValue::Union: case APValue::MemberPointer: case APValue::AddrLabelDiff: { @@ -8313,8 +8316,92 @@ class ExprEvaluatorBase return; VisitIgnoredValue(E); } + + /// Visit a metafunction evaluation (P2996). + bool VisitCXXMetafunctionExpr(const CXXMetafunctionExpr *E); + + bool VisitCXXIndeterminateSpliceExpr(const CXXIndeterminateSpliceExpr *E) { + return this->Visit(E->getOperand()); + } + + bool VisitCXXExprSpliceExpr(const CXXExprSpliceExpr *E) { + return this->Visit(E->getOperand()); + } + + bool VisitStackLocationExpr(const StackLocationExpr *E); }; +/// EvaluateAsRValue - Try to evaluate this expression, performing an implicit +/// lvalue-to-rvalue cast if it is an lvalue. +template +bool ExprEvaluatorBase::VisitCXXMetafunctionExpr( + const CXXMetafunctionExpr *E) { + EvalInfo &Info = this->Info; + auto Evaluator = [&Info](APValue &Result, const Expr *E, + bool ConvertToRValue) { + assert(!E->isValueDependent()); + + if (E->getType().isNull()) + return false; + + if (!CheckLiteralType(Info, E)) + return false; + + if (Info.EnableNewConstInterp) { + if (!Info.Ctx.getInterpContext().evaluateAsRValue(Info, E, Result)) + return false; + } else { + if (!::Evaluate(Result, Info, E)) + return false; + } + + if (ConvertToRValue) { + if (E->isGLValue()) { + LValue LV; + LV.setFrom(Info.Ctx, Result); + if (!handleLValueToRValueConversion(Info, E, E->getType(), LV, Result)) + return false; + } + + // Check this core constant expression is a constant expression. + return CheckConstantExpression(Info, E->getExprLoc(), E->getType(), + Result, ConstantExprKind::Normal); + } + return true; + }; + + // Construct array of arguments. + SmallVector Args(E->getNumArgs() - 1); + for (std::size_t I = 1; I < E->getNumArgs(); ++I) { + Args[I - 1] = E->getArg(I); + } + + if (Info.checkingPotentialConstantExpression()) + return false; + + // Evaluate the metafunction. + APValue Result; + const CXXMetafunctionExpr::ImplFn &Implementation = E->getImpl(); + if (Implementation(Result, Evaluator, E->getResultType(), E->getSourceRange(), + Args)) { + return Error(E); + } + return DerivedSuccess(Result, E); +} + +template +bool ExprEvaluatorBase::VisitStackLocationExpr( + const StackLocationExpr *E) { + CallStackFrame *Frame = Info.CurrentCall; + for (int Offset = E->getFrameOffset(); Frame && Offset > 0; --Offset) + Frame = Frame->Caller; + if (!Frame) + return Error(E); + + return DerivedSuccess(APValue(ReflectionValue::RK_declaration, + Frame->Callee), E); +} + } // namespace //===----------------------------------------------------------------------===// @@ -8514,6 +8601,8 @@ class LValueExprEvaluator return HandleDynamicCast(Info, cast(E), Result); } } + + bool VisitValueOfLValueExpr(const ValueOfLValueExpr *E); }; } // end anonymous namespace @@ -8927,6 +9016,20 @@ bool LValueExprEvaluator::VisitBinAssign(const BinaryOperator *E) { NewVal); } +bool LValueExprEvaluator::VisitValueOfLValueExpr(const ValueOfLValueExpr *E) { + CallStackFrame *Frame = Info.CurrentCall; + do { + if (Frame->getCurrentTemporary(E->getValueDecl())) { + unsigned Version = Frame->getCurrentTemporaryVersion(E->getValueDecl()); + + APValue::LValueBase LV(E->getValueDecl(), Frame->Index, Version); + return Success(LV); + } + } while ((Frame = Frame->Caller)); + + return Success(E->getValueDecl()); +} + //===----------------------------------------------------------------------===// // Pointer Evaluation //===----------------------------------------------------------------------===// @@ -10141,7 +10244,8 @@ bool MemberPointerExprEvaluator::VisitCastExpr(const CastExpr *E) { bool MemberPointerExprEvaluator::VisitUnaryAddrOf(const UnaryOperator *E) { // C++11 [expr.unary.op]p3 has very strict rules on how the address of a // member can be formed. - return Success(cast(E->getSubExpr())->getDecl()); + Expr *SubExpr = E->getSubExpr()->IgnoreExprSplices(); + return Success(cast(SubExpr)->getDecl()); } //===----------------------------------------------------------------------===// @@ -11641,6 +11745,7 @@ GCCTypeClass EvaluateBuiltinClassifyType(QualType T, case BuiltinType::ULong: case BuiltinType::ULongLong: case BuiltinType::UInt128: + case BuiltinType::MetaInfo: return GCCTypeClass::Integer; case BuiltinType::UShortAccum: @@ -13648,6 +13753,19 @@ EvaluateComparisonBinaryOperator(EvalInfo &Info, const BinaryOperator *E, return Success(CmpResult::Equal, E); } + if (LHSTy->isReflectionType() && RHSTy->isReflectionType()) { + APValue LHSValue, RHSValue; + if (!Evaluate(LHSValue, Info, E->getLHS())) + return false; + if (!Evaluate(RHSValue, Info, E->getRHS())) + return false; + + if (LHSValue.getReflection() == RHSValue.getReflection()) + return Success(CmpResult::Equal, E); + else + return Success(CmpResult::Unequal, E); + } + return DoAfter(); } @@ -15472,6 +15590,94 @@ static bool EvaluateVoid(const Expr *E, EvalInfo &Info) { return VoidExprEvaluator(Info).Visit(E); } +//===----------------------------------------------------------------------===// +// Reflection expression evaluation +//===----------------------------------------------------------------------===// + +namespace { +class ReflectionEvaluator + : public ExprEvaluatorBase { + + using BaseType = ExprEvaluatorBase; + + APValue &Result; +public: + ReflectionEvaluator(EvalInfo &E, APValue &Result) + : ExprEvaluatorBaseTy(E), Result(Result) {} + + bool Success(const APValue &V, const Expr *e) { + return Success(V); + } + + bool Success(const APValue &V) { + Result = V; + return true; + } + + bool VisitCXXReflectExpr(const CXXReflectExpr *E); + bool VisitCXXMetafunctionExpr(const CXXMetafunctionExpr *E); + bool VisitCXXIndeterminateSpliceExpr(const CXXIndeterminateSpliceExpr *E); + bool VisitCXXExprSpliceExpr(const CXXExprSpliceExpr *E); +}; + +bool ReflectionEvaluator::VisitCXXReflectExpr(const CXXReflectExpr *E) { + const ReflectionValue &Ref = E->getOperand(); + switch (Ref.getKind()) { + case ReflectionValue::RK_type: { + APValue Result(ReflectionValue::RK_type, Ref.getAsType().getAsOpaquePtr()); + return Success(Result, E); + } + case ReflectionValue::RK_const_value: { + APValue Result(ReflectionValue::RK_const_value, Ref.getAsConstValueExpr()); + return Success(Result, E); + } + case ReflectionValue::RK_declaration: { + APValue Result(ReflectionValue::RK_declaration, Ref.getAsDecl()); + return Success(Result, E); + } + case ReflectionValue::RK_template: { + APValue Result(ReflectionValue::RK_template, + Ref.getAsTemplate().getAsVoidPointer()); + return Success(Result, E); + } + case ReflectionValue::RK_namespace: { + APValue Result(ReflectionValue::RK_namespace, Ref.getAsNamespace()); + return Success(Result, E); + } + case ReflectionValue::RK_base_specifier: { + APValue Result(ReflectionValue::RK_base_specifier, + Ref.getAsBaseSpecifier()); + return Success(Result, E); + } + case ReflectionValue::RK_data_member_spec: { + APValue Result(ReflectionValue::RK_data_member_spec, + Ref.getAsDataMemberSpec()); + return Success(Result, E); + } + } + llvm_unreachable("invalid reflection"); +} + +bool ReflectionEvaluator::VisitCXXMetafunctionExpr( + const CXXMetafunctionExpr *E) { + return BaseType::VisitCXXMetafunctionExpr(E); +} + +bool ReflectionEvaluator::VisitCXXIndeterminateSpliceExpr( + const CXXIndeterminateSpliceExpr *E) { + return BaseType::VisitCXXIndeterminateSpliceExpr(E); +} + +bool ReflectionEvaluator::VisitCXXExprSpliceExpr(const CXXExprSpliceExpr *E) { + return BaseType::VisitCXXExprSpliceExpr(E); +} +} // end anonymous namespace + +static bool EvaluateReflection(const Expr *E, APValue &Result, EvalInfo &Info) { + assert(E->isPRValue() && E->getType()->isReflectionType()); + return ReflectionEvaluator(Info, Result).Visit(E); +} + //===----------------------------------------------------------------------===// // Top level Expr::EvaluateAsRValue method. //===----------------------------------------------------------------------===// @@ -15492,6 +15698,9 @@ static bool Evaluate(APValue &Result, EvalInfo &Info, const Expr *E) { } else if (T->isIntegralOrEnumerationType()) { if (!IntExprEvaluator(Info, Result).Visit(E)) return false; + } else if (T->isReflectionType()) { + if (!EvaluateReflection(E, Result, Info)) + return false; } else if (T->hasPointerRepresentation()) { LValue LV; if (!EvaluatePointer(E, LV, Info)) @@ -16208,6 +16417,7 @@ static ICEDiag CheckICE(const Expr* E, const ASTContext &Ctx) { case Expr::CoyieldExprClass: case Expr::SYCLUniqueStableNameExprClass: case Expr::CXXParenListInitExprClass: + case Expr::CXXDependentMemberSpliceExprClass: return ICEDiag(IK_NotICE, E->getBeginLoc()); case Expr::InitListExprClass: { @@ -16252,6 +16462,12 @@ static ICEDiag CheckICE(const Expr* E, const ASTContext &Ctx) { case Expr::ArrayTypeTraitExprClass: case Expr::ExpressionTraitExprClass: case Expr::CXXNoexceptExprClass: + case Expr::CXXReflectExprClass: + case Expr::CXXMetafunctionExprClass: + case Expr::CXXIndeterminateSpliceExprClass: + case Expr::CXXExprSpliceExprClass: + case Expr::StackLocationExprClass: + case Expr::ValueOfLValueExprClass: return NoDiag(); case Expr::CallExprClass: case Expr::CXXOperatorCallExprClass: { diff --git a/clang/lib/AST/ItaniumMangle.cpp b/clang/lib/AST/ItaniumMangle.cpp index d632c697fa20d..139ab868d7cd1 100644 --- a/clang/lib/AST/ItaniumMangle.cpp +++ b/clang/lib/AST/ItaniumMangle.cpp @@ -1,5 +1,7 @@ //===--- ItaniumMangle.cpp - Itanium C++ Name Mangling ----------*- C++ -*-===// // +// Copyright 2024 Bloomberg Finance L.P. +// // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception @@ -25,6 +27,7 @@ #include "clang/AST/ExprCXX.h" #include "clang/AST/ExprConcepts.h" #include "clang/AST/ExprObjC.h" +#include "clang/AST/LocInfoType.h" #include "clang/AST/Mangle.h" #include "clang/AST/TypeLoc.h" #include "clang/Basic/ABI.h" @@ -604,6 +607,7 @@ class CXXNameMangler { void mangleInitListElements(const InitListExpr *InitList); void mangleRequirement(SourceLocation RequiresExprLoc, const concepts::Requirement *Req); + void mangleReflection(const ReflectionValue &R); void mangleExpression(const Expr *E, unsigned Arity = UnknownArity, bool AsTemplateArg = false); void mangleCXXCtorType(CXXCtorType T, const CXXRecordDecl *InheritedFrom); @@ -1408,6 +1412,8 @@ void CXXNameMangler::mangleUnresolvedPrefix(NestedNameSpecifier *qualifier, mangleSourceName(qualifier->getAsIdentifier()); // An Identifier has no type information, so we can't emit abi tags for it. break; + case NestedNameSpecifier::IndeterminateSplice: + llvm_unreachable("should not get this far"); } // If this was the innermost part of the NNS, and we fell out to @@ -1591,6 +1597,14 @@ void CXXNameMangler::mangleUnqualifiedName( break; } + if (const FieldDecl *FD = dyn_cast(ND)) { + if (auto *Id = FD->getIdentifier()) + mangleSourceName(Id); + else // Hack a mangling of an unnamed field (e.g., lambda data member). + Out << "__" << uintptr_t(FD); + break; + } + // Class extensions have no name as a category, and it's possible // for them to be the semantic parent of certain declarations // (primarily, tag decls defined within declarations). Such @@ -2164,7 +2178,7 @@ void CXXNameMangler::manglePrefix(NestedNameSpecifier *qualifier) { manglePrefix(QualType(qualifier->getAsType(), 0)); return; - case NestedNameSpecifier::Identifier: + case NestedNameSpecifier::Identifier: { // Clang 14 and before did not consider this substitutable. bool Clang14Compat = isCompatibleWith(LangOptions::ClangABI::Ver14); if (!Clang14Compat && mangleSubstitution(qualifier)) @@ -2181,6 +2195,9 @@ void CXXNameMangler::manglePrefix(NestedNameSpecifier *qualifier) { addSubstitution(qualifier); return; } + case NestedNameSpecifier::IndeterminateSplice: + llvm_unreachable("should not get this far"); + } llvm_unreachable("unexpected nested name specifier"); } @@ -2450,6 +2467,7 @@ bool CXXNameMangler::mangleUnresolvedTypeOrSimpleId(QualType Ty, case Type::TypeOfExpr: case Type::TypeOf: case Type::Decltype: + case Type::ReflectionSplice: case Type::PackIndexing: case Type::TemplateTypeParm: case Type::UnaryTransform: @@ -3326,9 +3344,14 @@ void CXXNameMangler::mangleType(const BuiltinType *T) { Out << TI->getIbm128Mangling(); break; } - case BuiltinType::NullPtr: + case BuiltinType::NullPtr: { Out << "Dn"; break; + } + case BuiltinType::MetaInfo: { + Out << "Dm"; + break; + } #define BUILTIN_TYPE(Id, SingletonId) #define PLACEHOLDER_TYPE(Id, SingletonId) \ @@ -4362,6 +4385,16 @@ void CXXNameMangler::mangleType(const DecltypeType *T) { Out << 'E'; } +void CXXNameMangler::mangleType(const ReflectionSpliceType *T) { + // ::= RT E # typename of an expression + Out << "RT"; + // FIXME(P2996): This should probably mangle 'UnderlyingType' instead of + // 'Operand', but this is crashing the compiler. Revisit this, definitely + // something wrong here. + mangleExpression(T->getOperand()); + Out << "E"; +} + void CXXNameMangler::mangleType(const UnaryTransformType *T) { // If this is dependent, we need to record that. If not, we simply // mangle it as the underlying type since they are equivalent. @@ -4619,6 +4652,76 @@ void CXXNameMangler::mangleRequirement(SourceLocation RequiresExprLoc, } } +void CXXNameMangler::mangleReflection(const ReflectionValue &R) { + Out << 'M'; + + switch (R.getKind()) { + case ReflectionValue::RK_type: { + Out << 't'; + QualType QT = R.getAsType(); + + if (const TypedefType *TDT = dyn_cast(QT)) { + mangleQualifiers(QT.getQualifiers()); + mangleNameWithAbiTags(TDT->getDecl(), nullptr); + break; + } + Context.mangleCanonicalTypeName(QT, Out, false); + break; + } + case ReflectionValue::RK_const_value: + Out << 'e'; + mangleExpression(R.getAsConstValueExpr()); + break; + case ReflectionValue::RK_declaration: { + Out << 'd'; + + Decl *D = R.getAsDecl(); + if (auto * ED = dyn_cast(D)) { + mangleIntegerLiteral(ED->getType(), ED->getInitVal()); + } else { + mangle(cast(D)); + } + break; + } + case ReflectionValue::RK_template: { + Out << 't'; + + ArrayRef Args; + mangleTemplateName(R.getAsTemplate().getAsTemplateDecl(), Args); + break; + } + case ReflectionValue::RK_namespace: { + Out << 'n'; + if (auto *ND = dyn_cast(R.getAsNamespace())) + mangleNameWithAbiTags(ND, nullptr); + // Otherwise, this is the global namespace. + Out << '$'; + break; + } + case ReflectionValue::RK_base_specifier: { + Out << 'b'; + Context.mangleCanonicalTypeName(R.getAsBaseSpecifier()->getType(), Out, + false); + break; + } + case ReflectionValue::RK_data_member_spec: { + Out << "sdm"; + + TagDataMemberSpec *TDMS = R.getAsDataMemberSpec(); + Context.mangleCanonicalTypeName(TDMS->Ty, Out, false); + Out << TDMS->IsStatic; + if (TDMS->Name) + Out << "N$" << (*TDMS->Name) << '$'; + if (TDMS->Alignment) + Out << 'A' << (*TDMS->Alignment); + if (TDMS->BitWidth) + Out << 'B' << (*TDMS->BitWidth); + break; + } + } + Out << 'E'; +} + void CXXNameMangler::mangleExpression(const Expr *E, unsigned Arity, bool AsTemplateArg) { // ::= @@ -4720,13 +4823,33 @@ void CXXNameMangler::mangleExpression(const Expr *E, unsigned Arity, case Expr::OMPIteratorExprClass: case Expr::CXXInheritedCtorInitExprClass: case Expr::CXXParenListInitExprClass: + case Expr::CXXMetafunctionExprClass: + case Expr::CXXExprSpliceExprClass: + case Expr::CXXDependentMemberSpliceExprClass: + case Expr::StackLocationExprClass: + case Expr::ValueOfLValueExprClass: case Expr::PackIndexingExprClass: llvm_unreachable("unexpected statement kind"); - case Expr::ConstantExprClass: - E = cast(E)->getSubExpr(); + case Expr::CXXReflectExprClass: + mangleReflection(cast(E)->getOperand()); + break; + + case Expr::CXXIndeterminateSpliceExprClass: + E = cast(E)->getOperand(); goto recurse; + case Expr::ConstantExprClass: + if (const Expr *SubExpr = cast(E)->getSubExpr()) { + E = SubExpr; + goto recurse; + } else { + APValue Value = cast(E)->getAPValueResult(); + mangleValueInTemplateArg(E->getType(), Value, /*TopLevel=*/true, + /*NeedExactType=*/true); + break; + } + // FIXME: invent manglings for all these. case Expr::BlockExprClass: case Expr::ChooseExprClass: @@ -6039,6 +6162,7 @@ void CXXNameMangler::mangleTemplateArg(TemplateArgument A, bool NeedExactType) { // ::= X E # expression // ::= # simple expressions // ::= J * E # argument pack + // ::= M ... # reflection if (!A.isInstantiationDependent() || A.isDependent()) A = Context.getASTContext().getCanonicalTemplateArgument(A); @@ -6064,6 +6188,10 @@ void CXXNameMangler::mangleTemplateArg(TemplateArgument A, bool NeedExactType) { case TemplateArgument::Integral: mangleIntegerLiteral(A.getIntegralType(), A.getAsIntegral()); break; + case TemplateArgument::Reflection: { + mangleReflection(A.getAsReflection()); + break; + } case TemplateArgument::Declaration: { // ::= L E # external name ValueDecl *D = A.getAsDecl(); @@ -6163,6 +6291,7 @@ static bool isZeroInitialized(QualType T, const APValue &V) { case APValue::None: case APValue::Indeterminate: case APValue::AddrLabelDiff: + case APValue::Reflection: return false; case APValue::Struct: { @@ -6630,7 +6759,7 @@ void CXXNameMangler::mangleValueInTemplateArg(QualType T, const APValue &V, break; } - case APValue::MemberPointer: + case APValue::MemberPointer: { // Proposed in https://github.com/itanium-cxx-abi/cxx-abi/issues/47. if (!V.getMemberPointerDecl()) { mangleNullPointer(T); @@ -6664,6 +6793,12 @@ void CXXNameMangler::mangleValueInTemplateArg(QualType T, const APValue &V, break; } + case APValue::Reflection: { + mangleReflection(V.getReflection()); + break; + } + } + if (TopLevel && !IsPrimaryExpr) Out << 'E'; } diff --git a/clang/lib/AST/MicrosoftMangle.cpp b/clang/lib/AST/MicrosoftMangle.cpp index a0bb04e69c9be..128c3d501fe0f 100644 --- a/clang/lib/AST/MicrosoftMangle.cpp +++ b/clang/lib/AST/MicrosoftMangle.cpp @@ -1,5 +1,7 @@ //===--- MicrosoftMangle.cpp - Microsoft Visual C++ Name Mangling ---------===// // +// Copyright 2024 Bloomberg Finance L.P. +// // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception @@ -22,6 +24,7 @@ #include "clang/AST/Expr.h" #include "clang/AST/ExprCXX.h" #include "clang/AST/GlobalDecl.h" +#include "clang/AST/LocInfoType.h" #include "clang/AST/Mangle.h" #include "clang/AST/VTableBuilder.h" #include "clang/Basic/ABI.h" @@ -456,6 +459,7 @@ class MicrosoftCXXNameMangler { const NamedDecl *Parm); void mangleTemplateArgValue(QualType T, const APValue &V, TplArgKind, bool WithScalarType = false); + void mangleReflection(const ReflectionValue &R); void mangleObjCProtocol(const ObjCProtocolDecl *PD); void mangleObjCLifetime(const QualType T, Qualifiers Quals, @@ -1650,6 +1654,7 @@ void MicrosoftCXXNameMangler::mangleTemplateArg(const TemplateDecl *TD, // ::= H // ::= I // ::= J + // ::= M // // ::= [] // @@ -1698,6 +1703,10 @@ void MicrosoftCXXNameMangler::mangleTemplateArg(const TemplateDecl *TD, cast(Parm), T); break; } + case TemplateArgument::Reflection: { + mangleReflection(TA.getAsReflection()); + break; + } case TemplateArgument::NullPtr: { QualType T = TA.getNullPtrType(); if (const MemberPointerType *MPT = T->getAs()) { @@ -2005,6 +2014,10 @@ void MicrosoftCXXNameMangler::mangleTemplateArgValue(QualType T, case APValue::AddrLabelDiff: case APValue::FixedPoint: break; + + case APValue::Reflection: + llvm_unreachable("reflection arguments should be separately handled"); + return; } DiagnosticsEngine &Diags = Context.getDiags(); @@ -2013,6 +2026,39 @@ void MicrosoftCXXNameMangler::mangleTemplateArgValue(QualType T, Diags.Report(DiagID); } +void MicrosoftCXXNameMangler::mangleReflection(const ReflectionValue &R) { + Out << 'M'; + + const void *opaque = R.getOpaqueValue(); + switch (R.getKind()) { + case ReflectionValue::RK_type: { + Out << 't'; + QualType QT = QualType::getFromOpaquePtr(opaque); + if (const LocInfoType *LIT = dyn_cast(QT)) { + QT = LIT->getType(); + } + + if (const ElaboratedType *UD = dyn_cast(QT)) { + if (const TypedefType *TDT = dyn_cast(UD->getNamedType())) { + mangleQualifiers(QT.getQualifiers(), false); + mangleName(TDT->getDecl()); // <- not sure if this is right. + break; + } + } + Context.mangleCanonicalTypeName(QT, Out, false); + break; + } + case ReflectionValue::RK_const_value: + case ReflectionValue::RK_declaration: + case ReflectionValue::RK_template: + case ReflectionValue::RK_namespace: + case ReflectionValue::RK_base_specifier: + case ReflectionValue::RK_data_member_spec: + llvm_unreachable("unimplemented"); + } + Out << 'E'; +} + void MicrosoftCXXNameMangler::mangleObjCProtocol(const ObjCProtocolDecl *PD) { llvm::SmallString<64> TemplateMangling; llvm::raw_svector_ostream Stream(TemplateMangling); @@ -2577,6 +2623,10 @@ void MicrosoftCXXNameMangler::mangleType(const BuiltinType *T, Qualifiers, Out << "$$T"; break; + case BuiltinType::MetaInfo: + Out << "$$M"; + break; + case BuiltinType::Float16: mangleArtificialTagType(TagTypeKind::Struct, "_Float16", {"__clang"}); break; @@ -3446,6 +3496,15 @@ void MicrosoftCXXNameMangler::mangleType(const DecltypeType *T, Qualifiers, << Range; } +void MicrosoftCXXNameMangler::mangleType(const ReflectionSpliceType *T, + Qualifiers, SourceRange Range) { + DiagnosticsEngine &Diags = Context.getDiags(); + unsigned DiagID = Diags.getCustomDiagID(DiagnosticsEngine::Error, + "cannot mangle this [: expr :] yet"); + Diags.Report(Range.getBegin(), DiagID) + << Range; +} + void MicrosoftCXXNameMangler::mangleType(const UnaryTransformType *T, Qualifiers, SourceRange Range) { DiagnosticsEngine &Diags = Context.getDiags(); diff --git a/clang/lib/AST/NSAPI.cpp b/clang/lib/AST/NSAPI.cpp index 86dee540e9e29..ea44f1f1d2cb0 100644 --- a/clang/lib/AST/NSAPI.cpp +++ b/clang/lib/AST/NSAPI.cpp @@ -459,6 +459,7 @@ NSAPI::getNSNumberFactoryMethodKind(QualType T) const { case BuiltinType::Float128: case BuiltinType::Ibm128: case BuiltinType::NullPtr: + case BuiltinType::MetaInfo: case BuiltinType::ObjCClass: case BuiltinType::ObjCId: case BuiltinType::ObjCSel: diff --git a/clang/lib/AST/NestedNameSpecifier.cpp b/clang/lib/AST/NestedNameSpecifier.cpp index 36f2c47b30005..1917c7b8bff46 100644 --- a/clang/lib/AST/NestedNameSpecifier.cpp +++ b/clang/lib/AST/NestedNameSpecifier.cpp @@ -1,5 +1,7 @@ //===- NestedNameSpecifier.cpp - C++ nested name specifiers ---------------===// // +// Copyright 2024 Bloomberg Finance L.P. +// // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception @@ -17,6 +19,7 @@ #include "clang/AST/DeclCXX.h" #include "clang/AST/DeclTemplate.h" #include "clang/AST/DependenceFlags.h" +#include "clang/AST/ExprCXX.h" #include "clang/AST/PrettyPrinter.h" #include "clang/AST/TemplateName.h" #include "clang/AST/Type.h" @@ -140,6 +143,18 @@ NestedNameSpecifier::SuperSpecifier(const ASTContext &Context, return FindOrInsert(Context, Mockup); } +NestedNameSpecifier * +NestedNameSpecifier::IndeterminateSpliceSpecifier( + const ASTContext &Context, const CXXIndeterminateSpliceExpr *Expr) { + assert(Expr && "Expr cannot be NULL"); + NestedNameSpecifier Mockup; + Mockup.Prefix.setPointer(nullptr); + Mockup.Prefix.setInt(StoredIndeterminateSplice); + Mockup.Specifier = const_cast(Expr); + + return FindOrInsert(Context, Mockup); +} + NestedNameSpecifier::SpecifierKind NestedNameSpecifier::getKind() const { if (!Specifier) return Global; @@ -160,8 +175,10 @@ NestedNameSpecifier::SpecifierKind NestedNameSpecifier::getKind() const { case StoredTypeSpecWithTemplate: return TypeSpecWithTemplate; - } + case StoredIndeterminateSplice: + return IndeterminateSplice; + } llvm_unreachable("Invalid NNS Kind!"); } @@ -185,6 +202,7 @@ NamespaceAliasDecl *NestedNameSpecifier::getAsNamespaceAlias() const { CXXRecordDecl *NestedNameSpecifier::getAsRecordDecl() const { switch (Prefix.getInt()) { case StoredIdentifier: + case StoredIndeterminateSplice: return nullptr; case StoredDecl: @@ -211,10 +229,14 @@ NestedNameSpecifierDependence NestedNameSpecifier::getDependence() const { } case Namespace: - case NamespaceAlias: case Global: return NestedNameSpecifierDependence::None; + case NamespaceAlias: + return getAsNamespaceAlias()->isDependent() ? + NestedNameSpecifierDependence::Dependent : + NestedNameSpecifierDependence::None; + case Super: { CXXRecordDecl *RD = static_cast(Specifier); for (const auto &Base : RD->bases()) @@ -227,6 +249,9 @@ NestedNameSpecifierDependence NestedNameSpecifier::getDependence() const { case TypeSpec: case TypeSpecWithTemplate: return toNestedNameSpecifierDependendence(getAsType()->getDependence()); + + case IndeterminateSplice: + return toNestedNameSpecifierDependence(getAsSpliceExpr()->getDependence()); } llvm_unreachable("Invalid NNS Kind!"); } @@ -331,6 +356,11 @@ void NestedNameSpecifier::print(raw_ostream &OS, const PrintingPolicy &Policy, } break; } + + case IndeterminateSplice: { + OS << "[: " << getAsSpliceExpr() << " :]"; + break; + } } OS << "::"; @@ -374,7 +404,8 @@ NestedNameSpecifierLoc::getLocalDataLength(NestedNameSpecifier *Qualifier) { case NestedNameSpecifier::TypeSpecWithTemplate: case NestedNameSpecifier::TypeSpec: - // The "void*" that points at the TypeLoc data. + case NestedNameSpecifier::IndeterminateSplice: + // The "void*" that points at the TypeLoc or Expr data. // Note: the 'template' keyword is part of the TypeLoc. Length += sizeof(void *); break; @@ -445,6 +476,15 @@ SourceRange NestedNameSpecifierLoc::getLocalSourceRange() const { return SourceRange(TL.getBeginLoc(), LoadSourceLocation(Data, Offset + sizeof(void*))); } + case NestedNameSpecifier::IndeterminateSplice: { + // The "void*" that points at the Expr data. + const CXXIndeterminateSpliceExpr *Splice = + reinterpret_cast(LoadPointer(Data, + Offset)); + return SourceRange( + Splice->getLSpliceLoc(), + LoadSourceLocation(Data, Offset + sizeof(void*))); + } } llvm_unreachable("Invalid NNS Kind!"); @@ -461,6 +501,14 @@ TypeLoc NestedNameSpecifierLoc::getTypeLoc() const { return TypeLoc(Qualifier->getAsType(), TypeData); } +const CXXIndeterminateSpliceExpr * +NestedNameSpecifierLoc::getSpliceExpr() const { + if (Qualifier->getKind() != NestedNameSpecifier::IndeterminateSplice) + return nullptr; + + return Qualifier->getAsSpliceExpr(); +} + static void Append(char *Start, char *End, char *&Buffer, unsigned &BufferSize, unsigned &BufferCapacity) { if (Start == End) @@ -628,6 +676,18 @@ void NestedNameSpecifierLocBuilder::MakeSuper(ASTContext &Context, SaveSourceLocation(ColonColonLoc, Buffer, BufferSize, BufferCapacity); } +void NestedNameSpecifierLocBuilder::MakeIndeterminateSplice( + ASTContext &Context, const CXXIndeterminateSpliceExpr *Expr, + SourceLocation ColonColonLoc) { + Representation = NestedNameSpecifier::IndeterminateSpliceSpecifier(Context, + Expr); + + // Push source-location info into the buffer. + SavePointer(const_cast(Expr), Buffer, + BufferSize, BufferCapacity); + SaveSourceLocation(ColonColonLoc, Buffer, BufferSize, BufferCapacity); +} + void NestedNameSpecifierLocBuilder::MakeTrivial(ASTContext &Context, NestedNameSpecifier *Qualifier, SourceRange R) { @@ -658,6 +718,14 @@ void NestedNameSpecifierLocBuilder::MakeTrivial(ASTContext &Context, break; } + case NestedNameSpecifier::IndeterminateSplice: { + SavePointer( + const_cast(NNS->getAsSpliceExpr()), + Buffer, BufferSize, BufferCapacity); + SaveSourceLocation(R.getBegin(), Buffer, BufferSize, BufferCapacity); + break; + } + case NestedNameSpecifier::Global: case NestedNameSpecifier::Super: break; diff --git a/clang/lib/AST/ODRHash.cpp b/clang/lib/AST/ODRHash.cpp index e159a1b00be55..2e3e812108302 100644 --- a/clang/lib/AST/ODRHash.cpp +++ b/clang/lib/AST/ODRHash.cpp @@ -134,6 +134,7 @@ void ODRHash::AddNestedNameSpecifier(const NestedNameSpecifier *NNS) { break; case NestedNameSpecifier::Global: case NestedNameSpecifier::Super: + case NestedNameSpecifier::IndeterminateSplice: // This is wrong. break; } } @@ -180,6 +181,9 @@ void ODRHash::AddTemplateArgument(TemplateArgument TA) { TA.getAsIntegral().Profile(ID); break; } + case TemplateArgument::Reflection: + TA.getAsReflection().Profile(ID); + break; case TemplateArgument::StructuralValue: AddQualType(TA.getStructuralValueType()); AddStructuralValue(TA.getAsStructuralValue()); diff --git a/clang/lib/AST/Reflection.cpp b/clang/lib/AST/Reflection.cpp new file mode 100644 index 0000000000000..b8d5e722fcfc1 --- /dev/null +++ b/clang/lib/AST/Reflection.cpp @@ -0,0 +1,174 @@ +//===--- Reflection.cpp - Classes for representing reflection ---*- C++ -*-===// +// +// Copyright 2024 Bloomberg Finance L.P. +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This file implements the ReflectionValue class. +// +//===----------------------------------------------------------------------===// + +#include "clang/AST/APValue.h" +#include "clang/AST/ASTContext.h" +#include "clang/AST/Decl.h" +#include "clang/AST/DeclTemplate.h" +#include "clang/AST/Expr.h" +#include "clang/AST/LocInfoType.h" +#include "clang/AST/Reflection.h" + +namespace clang { + +ReflectionValue::ReflectionValue(ReflectionKind Kind, void *Entity) + : Kind(Kind), Entity(Entity) { +} + +ReflectionValue::ReflectionValue(ReflectionValue const& Rhs) + : Kind(Rhs.Kind), Entity(Rhs.Entity) { +} + +ReflectionValue &ReflectionValue::operator=(ReflectionValue const&Rhs) { + Kind = Rhs.Kind; + Entity = Rhs.Entity; + + return *this; +} + +QualType ReflectionValue::getAsType() const { + assert(getKind() == RK_type && "not a type"); + + QualType QT = QualType::getFromOpaquePtr(Entity); + if (const auto *LIT = dyn_cast(QT)) { + assert(QT.getQualifiers() == LIT->getType().getQualifiers()); + QT = LIT->getType(); + } + if (const auto *ET = dyn_cast(QT)) { + QualType New = ET->getNamedType(); + New.setLocalFastQualifiers(QT.getLocalFastQualifiers()); + QT = New; + } + if (const auto *RST = dyn_cast(QT)) + QT = RST->getUnderlyingType(); + if (const auto *DTT = dyn_cast(QT)) { + QT = DTT->desugar(); + } + + return QT; +} + +void ReflectionValue::Profile(llvm::FoldingSetNodeID &ID) const { + ID.AddInteger(Kind); + switch (Kind) { + case RK_type: { + QualType QT = getAsType(); + QT.Profile(ID); + break; + } + case RK_const_value: + if (auto *RD = getAsConstValueExpr()->getType()->getAsRecordDecl(); + RD && RD->isLambda()) { + QualType(RD->getTypeForDecl(), 0).Profile(ID); + } + getAsConstValueExpr()->getAPValueResult().Profile(ID); + break; + case RK_declaration: + ID.AddPointer(getAsDecl()); + break; + case RK_template: + getAsTemplate().Profile(ID); + break; + case RK_namespace: + ID.AddPointer(getAsNamespace()); + break; + case RK_base_specifier: + ID.AddPointer(getAsBaseSpecifier()); + break; + case RK_data_member_spec: { + TagDataMemberSpec *TDMS = getAsDataMemberSpec(); + TDMS->Ty.Profile(ID); + ID.AddBoolean(TDMS->Name.has_value()); + if (TDMS->Name) + ID.AddString(TDMS->Name.value()); + ID.AddBoolean(TDMS->IsStatic); + ID.AddBoolean(TDMS->Alignment.has_value()); + if (TDMS->Alignment) + ID.AddBoolean(TDMS->Alignment.value()); + ID.AddBoolean(TDMS->BitWidth.has_value()); + if (TDMS->BitWidth) + ID.AddBoolean(TDMS->BitWidth.value()); + break; + } + } +} + +bool ReflectionValue::operator==(ReflectionValue const& Rhs) const { + if (getKind() != Rhs.getKind()) + return false; + + switch (getKind()) { + case RK_type: { + QualType LQT = getAsType(), RQT = Rhs.getAsType(); + if (LQT.getQualifiers() != RQT.getQualifiers()) + return false; + + if (auto *LTST = dyn_cast(LQT)) { + if (LTST->isTypeAlias()) { + auto *RTST = dyn_cast(RQT); + if (!RTST || !RTST->isTypeAlias() || + LTST->getTemplateName().getAsTemplateDecl() != + RTST->getTemplateName().getAsTemplateDecl()) + return false; + } + return declaresSameEntity(LQT->getAsRecordDecl(), RQT->getAsRecordDecl()); + } + + if (LQT->isTypedefNameType()) + return LQT == RQT; + + return LQT.getCanonicalType() == RQT.getCanonicalType(); + } + case RK_const_value: { + APValue LV = getAsConstValueExpr()->getAPValueResult(); + APValue RV = Rhs.getAsConstValueExpr()->getAPValueResult(); + + llvm::FoldingSetNodeID LID, RID; + LV.Profile(LID); + RV.Profile(RID); + + return LID == RID; + } + case RK_declaration: + return declaresSameEntity(getAsDecl(), Rhs.getAsDecl()); + case RK_template: + return declaresSameEntity(getAsTemplate().getAsTemplateDecl(), + Rhs.getAsTemplate().getAsTemplateDecl()); + case RK_namespace: + return declaresSameEntity(getAsNamespace(), Rhs.getAsNamespace()); + case RK_base_specifier: + return getAsBaseSpecifier() == Rhs.getAsBaseSpecifier(); + case RK_data_member_spec: + return getAsDataMemberSpec() == Rhs.getAsDataMemberSpec(); + } + llvm_unreachable("unknown reflection kind"); +} + +bool ReflectionValue::operator!=(ReflectionValue const& Rhs) const { + return !(*this == Rhs); +} + +bool TagDataMemberSpec::operator==(TagDataMemberSpec const &Rhs) const { + return (Ty == Ty && + IsStatic == Rhs.IsStatic && + Alignment == Rhs.Alignment && + BitWidth == Rhs.BitWidth && + Name == Rhs.Name); +} + +bool TagDataMemberSpec::operator!=(TagDataMemberSpec const &Rhs) const { + return !(*this == Rhs); +} + +} // end namespace clang diff --git a/clang/lib/AST/StmtPrinter.cpp b/clang/lib/AST/StmtPrinter.cpp index 74b18e50bf1f4..a23237037d70a 100644 --- a/clang/lib/AST/StmtPrinter.cpp +++ b/clang/lib/AST/StmtPrinter.cpp @@ -2603,6 +2603,49 @@ void StmtPrinter::VisitCoyieldExpr(CoyieldExpr *S) { PrintExpr(S->getOperand()); } +void StmtPrinter::VisitCXXReflectExpr(CXXReflectExpr *S) { + // FIXME: Make this better. + OS << "^(...)"; +} + +void StmtPrinter::VisitCXXMetafunctionExpr(CXXMetafunctionExpr *S) { + OS << "__metafunction("; + for (unsigned I = 0; I < S->getNumArgs(); ++S) { + PrintExpr(S->getArg(I)); + if (I + 1 != S->getNumArgs()) + OS << ", "; + } + OS << ")"; +} + +void StmtPrinter::VisitCXXIndeterminateSpliceExpr( + CXXIndeterminateSpliceExpr *S) { + OS << "[: "; + Visit(S->getOperand()); + OS << " :]"; +} + +void StmtPrinter::VisitCXXExprSpliceExpr(CXXExprSpliceExpr *S) { + OS << "[: "; + Visit(S->getOperand()); + OS << " :]"; +} + +void StmtPrinter::VisitCXXDependentMemberSpliceExpr( + CXXDependentMemberSpliceExpr *S) { + Visit(S->getBase()); + OS << "."; + Visit(S->getRHS()); +} + +void StmtPrinter::VisitStackLocationExpr(StackLocationExpr *S) { + OS << "StackLoc(" << S->getFrameOffset() << ")"; +} + +void StmtPrinter::VisitValueOfLValueExpr(ValueOfLValueExpr *S) { + OS << "ValueOfLValue()"; +} + // Obj-C void StmtPrinter::VisitObjCStringLiteral(ObjCStringLiteral *Node) { diff --git a/clang/lib/AST/StmtProfile.cpp b/clang/lib/AST/StmtProfile.cpp index d68547f444c52..27a803d2badff 100644 --- a/clang/lib/AST/StmtProfile.cpp +++ b/clang/lib/AST/StmtProfile.cpp @@ -2286,6 +2286,40 @@ void StmtProfiler::VisitOpaqueValueExpr(const OpaqueValueExpr *E) { VisitExpr(E); } +void StmtProfiler::VisitCXXReflectExpr(const CXXReflectExpr *E) { + VisitExpr(E); + + const ReflectionValue &Operand = E->getOperand(); + ID.AddInteger(Operand.getKind()); + ID.AddInteger(reinterpret_cast(Operand.getOpaqueValue())); +} + +void StmtProfiler::VisitCXXMetafunctionExpr(const CXXMetafunctionExpr *E) { + VisitExpr(E); +} + +void StmtProfiler::VisitCXXIndeterminateSpliceExpr( + const CXXIndeterminateSpliceExpr *E) { + VisitExpr(E); +} + +void StmtProfiler::VisitCXXExprSpliceExpr(const CXXExprSpliceExpr *E) { + VisitExpr(E); +} + +void StmtProfiler::VisitCXXDependentMemberSpliceExpr( + const CXXDependentMemberSpliceExpr *E) { + VisitExpr(E); +} + +void StmtProfiler::VisitStackLocationExpr(const StackLocationExpr *E) { + VisitExpr(E); +} + +void StmtProfiler::VisitValueOfLValueExpr(const ValueOfLValueExpr *E) { + VisitDecl(E->getValueDecl()); +} + void StmtProfiler::VisitTypoExpr(const TypoExpr *E) { VisitExpr(E); } @@ -2424,6 +2458,10 @@ void StmtProfiler::VisitTemplateArgument(const TemplateArgument &Arg) { Arg.getAsIntegral().Profile(ID); break; + case TemplateArgument::Reflection: + Arg.getAsReflection().Profile(ID); + break; + case TemplateArgument::StructuralValue: VisitType(Arg.getStructuralValueType()); // FIXME: Do we need to recursively decompose this ourselves? diff --git a/clang/lib/AST/TemplateBase.cpp b/clang/lib/AST/TemplateBase.cpp index 3310d7dc24c59..56648cd86128e 100644 --- a/clang/lib/AST/TemplateBase.cpp +++ b/clang/lib/AST/TemplateBase.cpp @@ -1,5 +1,7 @@ //===- TemplateBase.cpp - Common template AST class implementation --------===// // +// Copyright 2024 Bloomberg Finance L.P. +// // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception @@ -137,6 +139,40 @@ static void printIntegral(const TemplateArgument &TemplArg, raw_ostream &Out, Out << Val; } +/// Print a template reflection argument value. +/// +/// \param TemplArg the TemplateArgument instance to print. +/// +/// \param Out the raw_ostream instance to use for printing. +/// +/// \param Policy the printing policy for EnumConstantDecl printing. +/// +/// \param IncludeType If set, ensure that the type of the expression printed +/// matches the type of the template argument. +static void printReflection(const TemplateArgument &TemplArg, raw_ostream &Out, + const PrintingPolicy &Policy, bool IncludeType) { + // TODO(P2996): Implement this. + Out << "(reflection)"; +} + +/// Print a template indeterminate splice argument value. +/// +/// \param TemplArg the TemplateArgument instance to print. +/// +/// \param Out the raw_ostream instance to use for printing. +/// +/// \param Policy the printing policy for EnumConstantDecl printing. +/// +/// \param IncludeType If set, ensure that the type of the expression printed +/// matches the type of the template argument. +static void printIndeterminateSplice(const TemplateArgument &TemplArg, + raw_ostream &Out, + const PrintingPolicy &Policy, + bool IncludeType) { + // TODO(P2996): Implement this. + Out << "[:reflection-splice:]"; +} + static unsigned getArrayDepth(QualType type) { unsigned count = 0; while (const auto *arrayType = type->getAsArrayTypeUnsafe()) { @@ -198,6 +234,22 @@ void TemplateArgument::initFromIntegral(const ASTContext &Ctx, Integer.Type = Type.getAsOpaquePtr(); } +TemplateArgument::TemplateArgument(ASTContext &Ctx, + const ReflectionValue &Value, + bool IsDefaulted) { + ReflectionArg.Kind = Reflection; + ReflectionArg.IsDefaulted = IsDefaulted; + ReflectionArg.Type = Ctx.MetaInfoTy.getAsOpaquePtr(); + new (ReflectionArg.Value.buffer) ReflectionValue(Value); +} + +TemplateArgument::TemplateArgument(CXXIndeterminateSpliceExpr *Splice, + bool IsDefaulted) { + TypeOrValue.Kind = IndeterminateSplice; + TypeOrValue.IsDefaulted = IsDefaulted; + TypeOrValue.V = reinterpret_cast(Splice); +} + void TemplateArgument::initFromStructural(const ASTContext &Ctx, QualType Type, const APValue &V, bool IsDefaulted) { Value.Kind = StructuralValue; @@ -259,6 +311,15 @@ TemplateArgument::CreatePackCopy(ASTContext &Context, TemplateArgumentDependence TemplateArgument::getDependence() const { auto Deps = TemplateArgumentDependence::None; + + auto computeFromExpr = [](Expr *E) { + auto Deps = toTemplateArgumentDependence(E->getDependence()); + if (isa(E)) + Deps |= TemplateArgumentDependence::Dependent | + TemplateArgumentDependence::Instantiation; + return Deps; + }; + switch (getKind()) { case Null: llvm_unreachable("Should not have a NULL template argument"); @@ -288,15 +349,15 @@ TemplateArgumentDependence TemplateArgument::getDependence() const { case NullPtr: case Integral: + case Reflection: case StructuralValue: return TemplateArgumentDependence::None; + case IndeterminateSplice: + return computeFromExpr(getAsIndeterminateSplice()); + case Expression: - Deps = toTemplateArgumentDependence(getAsExpr()->getDependence()); - if (isa(getAsExpr())) - Deps |= TemplateArgumentDependence::Dependent | - TemplateArgumentDependence::Instantiation; - return Deps; + return computeFromExpr(getAsExpr()); case Pack: for (const auto &P : pack_elements()) @@ -319,6 +380,7 @@ bool TemplateArgument::isPackExpansion() const { case Null: case Declaration: case Integral: + case Reflection: case StructuralValue: case Pack: case Template: @@ -333,6 +395,9 @@ bool TemplateArgument::isPackExpansion() const { case Expression: return isa(getAsExpr()); + + case IndeterminateSplice: + return isa(getAsIndeterminateSplice()); } llvm_unreachable("Invalid TemplateArgument Kind!"); @@ -356,12 +421,16 @@ QualType TemplateArgument::getNonTypeTemplateArgumentType() const { case TemplateArgument::Type: case TemplateArgument::Template: case TemplateArgument::TemplateExpansion: + case TemplateArgument::IndeterminateSplice: case TemplateArgument::Pack: return QualType(); case TemplateArgument::Integral: return getIntegralType(); + case TemplateArgument::Reflection: + return getReflectionType(); + case TemplateArgument::Expression: return getAsExpr()->getType(); @@ -415,6 +484,16 @@ void TemplateArgument::Profile(llvm::FoldingSetNodeID &ID, getAsStructuralValue().Profile(ID); break; + case Reflection: + getAsReflection().Profile(ID); + getReflectionType().Profile(ID); + break; + + case IndeterminateSplice: + // TODO(P2996): Revisit this. + getAsIndeterminateSplice()->Profile(ID, Context, true); + break; + case Expression: getAsExpr()->Profile(ID, Context, true); break; @@ -449,6 +528,13 @@ bool TemplateArgument::structurallyEquals(const TemplateArgument &Other) const { return getIntegralType() == Other.getIntegralType() && getAsIntegral() == Other.getAsIntegral(); + case Reflection: + return getReflectionType() == Other.getReflectionType() && + getAsReflection() == Other.getAsReflection(); + + case IndeterminateSplice: + return false; // TODO(P2996): Revisit this. + case StructuralValue: { if (getStructuralValueType().getCanonicalType() != Other.getStructuralValueType().getCanonicalType()) @@ -486,6 +572,8 @@ TemplateArgument TemplateArgument::getPackExpansionPattern() const { case Declaration: case Integral: + case Reflection: + case IndeterminateSplice: case StructuralValue: case Pack: case Null: @@ -551,6 +639,14 @@ void TemplateArgument::print(const PrintingPolicy &Policy, raw_ostream &Out, printIntegral(*this, Out, Policy, IncludeType); break; + case Reflection: + printReflection(*this, Out, Policy, IncludeType); + break; + + case IndeterminateSplice: + getAsIndeterminateSplice()->printPretty(Out, nullptr, Policy); + break; + case Expression: getAsExpr()->printPretty(Out, nullptr, Policy); break; @@ -616,6 +712,12 @@ SourceRange TemplateArgumentLoc::getSourceRange() const { case TemplateArgument::Integral: return getSourceIntegralExpression()->getSourceRange(); + case TemplateArgument::Reflection: + return getSourceReflectionExpression()->getSourceRange(); + + case TemplateArgument::IndeterminateSplice: + return getSourceIndeterminateSpliceExpression()->getSourceRange(); + case TemplateArgument::StructuralValue: return getSourceStructuralValueExpression()->getSourceRange(); @@ -647,6 +749,14 @@ static const T &DiagTemplateArg(const T &DB, const TemplateArgument &Arg) { case TemplateArgument::Integral: return DB << toString(Arg.getAsIntegral(), 10); + case TemplateArgument::Reflection: + // TODO(P2996): Implement this. + return DB << "(reflection)"; + + case TemplateArgument::IndeterminateSplice: + // TODO(P2996): Implement this. + return DB << "[:reflection-splice:]"; + case TemplateArgument::StructuralValue: { // FIXME: We're guessing at LangOptions! SmallString<32> Str; diff --git a/clang/lib/AST/TemplateName.cpp b/clang/lib/AST/TemplateName.cpp index 2f0e4181e9408..04906166f399c 100644 --- a/clang/lib/AST/TemplateName.cpp +++ b/clang/lib/AST/TemplateName.cpp @@ -239,8 +239,11 @@ TemplateNameDependence TemplateName::getDependence() const { getAsQualifiedTemplateName()->getQualifier()->getDependence()); break; case TemplateName::NameKind::DependentTemplate: - D |= toTemplateNameDependence( - getAsDependentTemplateName()->getQualifier()->getDependence()); + if (getAsDependentTemplateName()->isIndeterminateSplice()) + D |= TemplateNameDependence::DependentInstantiation; + else + D |= toTemplateNameDependence( + getAsDependentTemplateName()->getQualifier()->getDependence()); break; case TemplateName::NameKind::SubstTemplateTemplateParmPack: D |= TemplateNameDependence::UnexpandedPack; @@ -281,7 +284,7 @@ bool TemplateName::containsUnexpandedParameterPack() const { return getDependence() & TemplateNameDependence::UnexpandedPack; } -void TemplateName::Profile(llvm::FoldingSetNodeID &ID) { +void TemplateName::Profile(llvm::FoldingSetNodeID &ID) const { if (const auto* USD = getAsUsingShadowDecl()) ID.AddPointer(USD->getCanonicalDecl()); else if (const auto *TD = getAsTemplateDecl()) diff --git a/clang/lib/AST/TextNodeDumper.cpp b/clang/lib/AST/TextNodeDumper.cpp index f498de6374348..37ddb5776cbaf 100644 --- a/clang/lib/AST/TextNodeDumper.cpp +++ b/clang/lib/AST/TextNodeDumper.cpp @@ -487,6 +487,7 @@ static bool isSimpleAPValue(const APValue &Value) { case APValue::Vector: case APValue::Array: case APValue::Struct: + case APValue::Reflection: return false; case APValue::Union: return isSimpleAPValue(Value.getUnionValue()); @@ -670,6 +671,9 @@ void TextNodeDumper::Visit(const APValue &Value, QualType Ty) { case APValue::AddrLabelDiff: OS << "AddrLabelDiff "; return; + case APValue::Reflection: + OS << "Reflection "; + return; } llvm_unreachable("Unknown APValue kind!"); } @@ -855,6 +859,9 @@ void clang::TextNodeDumper::dumpNestedNameSpecifier(const NestedNameSpecifier *N case NestedNameSpecifier::Super: OS << " Super"; break; + case NestedNameSpecifier::IndeterminateSplice: + OS << " IndeterminateSplice"; + break; } dumpNestedNameSpecifier(NNS->getPrefix()); diff --git a/clang/lib/AST/Type.cpp b/clang/lib/AST/Type.cpp index cb22c91a12aa8..51ff3f1f73a93 100644 --- a/clang/lib/AST/Type.cpp +++ b/clang/lib/AST/Type.cpp @@ -1,5 +1,7 @@ //===- Type.cpp - Type representation and manipulation --------------------===// // +// Copyright 2024 Bloomberg Finance L.P. +// // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception @@ -153,7 +155,7 @@ ArrayType::ArrayType(TypeClass tc, QualType et, QualType can, : TypeDependence::None) | (tc == DependentSizedArray ? TypeDependence::DependentInstantiation - : TypeDependence::None)), + : TypeDependence::None), et->isMetaType()), ElementType(et) { ArrayTypeBits.IndexTypeQuals = tq; ArrayTypeBits.SizeModifier = llvm::to_underlying(sm); @@ -266,7 +268,7 @@ DependentVectorType::DependentVectorType(QualType ElementType, TypeDependence::DependentInstantiation | ElementType->getDependence() | (SizeExpr ? toTypeDependence(SizeExpr->getDependence()) - : TypeDependence::None)), + : TypeDependence::None), ElementType->isMetaType()), ElementType(ElementType), SizeExpr(SizeExpr), Loc(Loc) { VectorTypeBits.VecKind = llvm::to_underlying(VecKind); } @@ -288,7 +290,7 @@ DependentSizedExtVectorType::DependentSizedExtVectorType(QualType ElementType, TypeDependence::DependentInstantiation | ElementType->getDependence() | (SizeExpr ? toTypeDependence(SizeExpr->getDependence()) - : TypeDependence::None)), + : TypeDependence::None), ElementType->isMetaType()), SizeExpr(SizeExpr), ElementType(ElementType), loc(loc) {} void @@ -307,7 +309,8 @@ DependentAddressSpaceType::DependentAddressSpaceType(QualType PointeeType, TypeDependence::DependentInstantiation | PointeeType->getDependence() | (AddrSpaceExpr ? toTypeDependence(AddrSpaceExpr->getDependence()) - : TypeDependence::None)), + : TypeDependence::None), + PointeeType->isMetaType()), AddrSpaceExpr(AddrSpaceExpr), PointeeType(PointeeType), loc(loc) {} void DependentAddressSpaceType::Profile(llvm::FoldingSetNodeID &ID, @@ -333,7 +336,8 @@ MatrixType::MatrixType(TypeClass tc, QualType matrixType, QualType canonType, ColumnExpr->containsUnexpandedParameterPack()) ? TypeDependence::UnexpandedPack : TypeDependence::None)) - : matrixType->getDependence())), + : matrixType->getDependence()), + matrixType->isMetaType()), ElementType(matrixType) {} ConstantMatrixType::ConstantMatrixType(QualType matrixType, unsigned nRows, @@ -371,18 +375,19 @@ VectorType::VectorType(QualType vecType, unsigned nElements, QualType canonType, VectorType::VectorType(TypeClass tc, QualType vecType, unsigned nElements, QualType canonType, VectorKind vecKind) - : Type(tc, canonType, vecType->getDependence()), ElementType(vecType) { + : Type(tc, canonType, vecType->getDependence(), vecType->isMetaType()), + ElementType(vecType) { VectorTypeBits.VecKind = llvm::to_underlying(vecKind); VectorTypeBits.NumElements = nElements; } BitIntType::BitIntType(bool IsUnsigned, unsigned NumBits) - : Type(BitInt, QualType{}, TypeDependence::None), IsUnsigned(IsUnsigned), - NumBits(NumBits) {} + : Type(BitInt, QualType{}, TypeDependence::None, /*MetaType=*/false), + IsUnsigned(IsUnsigned), NumBits(NumBits) {} DependentBitIntType::DependentBitIntType(bool IsUnsigned, Expr *NumBitsExpr) : Type(DependentBitInt, QualType{}, - toTypeDependence(NumBitsExpr->getDependence())), + toTypeDependence(NumBitsExpr->getDependence()), /*MetaType=*/false), ExprAndUnsigned(NumBitsExpr, IsUnsigned) {} bool DependentBitIntType::isUnsigned() const { @@ -631,6 +636,17 @@ bool Type::isStructureType() const { return false; } +bool Type::isMetaType() const { + const Type *CanonType = getCanonicalTypeInternal().getTypePtr(); + if (CanonType != this) + return CanonType->isMetaType(); + else if (TypeBits.MetaType) + return true; + else if (auto *RD = getAsRecordDecl()) + return RD->isMetaType(); + return false; +} + bool Type::isObjCBoxableRecordType() const { if (const auto *RT = getAs()) return RT->getDecl()->hasAttr(); @@ -791,7 +807,8 @@ bool Type::isObjCClassOrClassKindOfType() const { ObjCTypeParamType::ObjCTypeParamType(const ObjCTypeParamDecl *D, QualType can, ArrayRef protocols) - : Type(ObjCTypeParam, can, toSemanticDependence(can->getDependence())), + : Type(ObjCTypeParam, can, toSemanticDependence(can->getDependence()), + /*MetaType=*/false), OTPDecl(const_cast(D)) { initialize(protocols); } @@ -800,7 +817,8 @@ ObjCObjectType::ObjCObjectType(QualType Canonical, QualType Base, ArrayRef typeArgs, ArrayRef protocols, bool isKindOf) - : Type(ObjCObject, Canonical, Base->getDependence()), BaseType(Base) { + : Type(ObjCObject, Canonical, Base->getDependence(), /*MetaType=*/false), + BaseType(Base) { ObjCObjectTypeBits.IsKindOf = isKindOf; ObjCObjectTypeBits.NumTypeArgs = typeArgs.size(); @@ -3377,6 +3395,8 @@ StringRef BuiltinType::getName(const PrintingPolicy &Policy) const { return "char32_t"; case NullPtr: return Policy.NullptrTypeInNamespace ? "std::nullptr_t" : "nullptr_t"; + case MetaInfo: + return "meta::info"; case Overload: return ""; case BoundMember: @@ -3784,7 +3804,7 @@ void TypeCoupledDeclRefInfo::setFromOpaqueValue(void *V) { BoundsAttributedType::BoundsAttributedType(TypeClass TC, QualType Wrapped, QualType Canon) - : Type(TC, Canon, Wrapped->getDependence()), WrappedTy(Wrapped) {} + : Type(TC, Canon, Wrapped->getDependence(), false), WrappedTy(Wrapped) {} CountAttributedType::CountAttributedType( QualType Wrapped, QualType Canon, Expr *CountExpr, bool CountInBytes, @@ -3802,7 +3822,8 @@ CountAttributedType::CountAttributedType( TypedefType::TypedefType(TypeClass tc, const TypedefNameDecl *D, QualType Underlying, QualType can) - : Type(tc, can, toSemanticDependence(can->getDependence())), + : Type(tc, can, toSemanticDependence(can->getDependence()), + false), Decl(const_cast(D)) { assert(!isa(can) && "Invalid canonical type"); TypedefBits.hasTypeDifferentFromDecl = !Underlying.isNull(); @@ -3817,7 +3838,8 @@ QualType TypedefType::desugar() const { UsingType::UsingType(const UsingShadowDecl *Found, QualType Underlying, QualType Canon) - : Type(Using, Canon, toSemanticDependence(Canon->getDependence())), + : Type(Using, Canon, toSemanticDependence(Canon->getDependence()), + false), Found(const_cast(Found)) { UsingBits.hasTypeDifferentFromDecl = !Underlying.isNull(); if (!typeMatchesDecl()) @@ -3854,7 +3876,7 @@ TypeOfExprType::TypeOfExprType(Expr *E, TypeOfKind Kind, QualType Can) : Can, toTypeDependence(E->getDependence()) | (E->getType()->getDependence() & - TypeDependence::VariablyModified)), + TypeDependence::VariablyModified), E->getType()->isMetaType()), TOExpr(E) { TypeOfBits.IsUnqual = Kind == TypeOfKind::Unqualified; } @@ -3887,7 +3909,7 @@ DecltypeType::DecltypeType(Expr *E, QualType underlyingType, QualType can) (E->isInstantiationDependent() ? TypeDependence::Dependent : TypeDependence::None) | (E->getType()->getDependence() & - TypeDependence::VariablyModified)), + TypeDependence::VariablyModified), E->getType()->isMetaType()), E(E), UnderlyingType(underlyingType) {} bool DecltypeType::isSugared() const { return !E->isInstantiationDependent(); } @@ -3907,12 +3929,43 @@ void DependentDecltypeType::Profile(llvm::FoldingSetNodeID &ID, E->Profile(ID, Context, true); } +ReflectionSpliceType::ReflectionSpliceType(Expr *Operand, QualType T, + QualType Canon) + : Type(ReflectionSplice, Canon, toTypeDependence(Operand->getDependence()), + T->isMetaType()), + Operand(Operand), UnderlyingTy(T) { +} + +QualType ReflectionSpliceType::desugar() const { + if (isSugared()) + return getUnderlyingType(); + else + return QualType(this, 0); +} + +bool ReflectionSpliceType::isSugared() const { + // A reflected type is sugared if it's non-dependent. + return !Operand->isInstantiationDependent(); +} + +DependentReflectionSpliceType::DependentReflectionSpliceType( + const ASTContext &Context, Expr *Operand) + : ReflectionSpliceType(Operand, Context.DependentTy), Context(Context) { +} + +void DependentReflectionSpliceType::Profile(llvm::FoldingSetNodeID &ID, + const ASTContext &Context, + Expr *Operand) { + Operand->Profile(ID, Context, true); +} + PackIndexingType::PackIndexingType(const ASTContext &Context, QualType Canonical, QualType Pattern, Expr *IndexExpr, ArrayRef Expansions) : Type(PackIndexing, Canonical, - computeDependence(Pattern, IndexExpr, Expansions)), + computeDependence(Pattern, IndexExpr, Expansions), + !Canonical.isNull() ? Canonical->isMetaType() : false), Context(Context), Pattern(Pattern), IndexExpr(IndexExpr), Size(Expansions.size()) { @@ -3967,7 +4020,8 @@ void PackIndexingType::Profile(llvm::FoldingSetNodeID &ID, UnaryTransformType::UnaryTransformType(QualType BaseType, QualType UnderlyingType, UTTKind UKind, QualType CanonicalType) - : Type(UnaryTransform, CanonicalType, BaseType->getDependence()), + : Type(UnaryTransform, CanonicalType, BaseType->getDependence(), + BaseType->isMetaType()), BaseType(BaseType), UnderlyingType(UnderlyingType), UKind(UKind) {} DependentUnaryTransformType::DependentUnaryTransformType(const ASTContext &C, @@ -3978,7 +4032,8 @@ DependentUnaryTransformType::DependentUnaryTransformType(const ASTContext &C, TagType::TagType(TypeClass TC, const TagDecl *D, QualType can) : Type(TC, can, D->isDependentType() ? TypeDependence::DependentInstantiation - : TypeDependence::None), + : TypeDependence::None, + /*MetaType=*/false), decl(const_cast(D)) {} static TagDecl *getInterestingTagDecl(TagDecl *decl) { @@ -4111,7 +4166,7 @@ SubstTemplateTypeParmType::SubstTemplateTypeParmType( QualType Replacement, Decl *AssociatedDecl, unsigned Index, std::optional PackIndex) : Type(SubstTemplateTypeParm, Replacement.getCanonicalType(), - Replacement->getDependence()), + Replacement->getDependence(), /*MetaType=*/false), AssociatedDecl(AssociatedDecl) { SubstTemplateTypeParmTypeBits.HasNonCanonicalUnderlyingType = Replacement != getCanonicalTypeInternal(); @@ -4133,7 +4188,7 @@ SubstTemplateTypeParmPackType::SubstTemplateTypeParmPackType( const TemplateArgument &ArgPack) : Type(SubstTemplateTypeParmPack, Canon, TypeDependence::DependentInstantiation | - TypeDependence::UnexpandedPack), + TypeDependence::UnexpandedPack, /*MetaType=*/false), Arguments(ArgPack.pack_begin()), AssociatedDeclAndFinal(AssociatedDecl, Final) { SubstTemplateTypeParmPackTypeBits.Index = Index; @@ -4208,7 +4263,7 @@ TemplateSpecializationType::TemplateSpecializationType( ? TypeDependence::DependentInstantiation : toSemanticDependence(Canon->getDependence())) | (toTypeDependence(T.getDependence()) & - TypeDependence::UnexpandedPack)), + TypeDependence::UnexpandedPack), /*MetaType=*/false), Template(T) { TemplateSpecializationTypeBits.NumArgs = Args.size(); TemplateSpecializationTypeBits.TypeAlias = !AliasedType.isNull(); @@ -4650,6 +4705,7 @@ bool Type::canHaveNullability(bool ResultIfUnknown) const { case Type::DependentName: case Type::DependentTemplateSpecialization: case Type::Auto: + case Type::ReflectionSplice: return ResultIfUnknown; // Dependent template specializations could instantiate to pointer types. @@ -4709,6 +4765,7 @@ bool Type::canHaveNullability(bool ResultIfUnknown) const { #include "clang/Basic/WebAssemblyReferenceTypes.def" case BuiltinType::BuiltinFn: case BuiltinType::NullPtr: + case BuiltinType::MetaInfo: case BuiltinType::IncompleteMatrixIdx: case BuiltinType::OMPArraySection: case BuiltinType::OMPArrayShaping: diff --git a/clang/lib/AST/TypeLoc.cpp b/clang/lib/AST/TypeLoc.cpp index 21e152f6aea8a..c2eca96d58377 100644 --- a/clang/lib/AST/TypeLoc.cpp +++ b/clang/lib/AST/TypeLoc.cpp @@ -349,6 +349,7 @@ TypeSpecifierType BuiltinTypeLoc::getWrittenTypeSpec() const { case BuiltinType::WChar_S: case BuiltinType::WChar_U: return TST_wchar; + case BuiltinType::MetaInfo: case BuiltinType::UChar: case BuiltinType::UShort: case BuiltinType::UInt: @@ -588,6 +589,7 @@ void TemplateSpecializationTypeLoc::initializeArgLocs( llvm_unreachable("Impossible TemplateArgument"); case TemplateArgument::Integral: + case TemplateArgument::Reflection: case TemplateArgument::Declaration: case TemplateArgument::NullPtr: case TemplateArgument::StructuralValue: @@ -627,6 +629,12 @@ void TemplateSpecializationTypeLoc::initializeArgLocs( } } +void ReflectionSpliceTypeLoc::initializeLocal(ASTContext &Context, + SourceLocation Loc) { + setLSpliceLoc(Loc); + setRSpliceLoc(Loc); +} + // Builds a ConceptReference where all locations point at the same token, // for use in trivial TypeSourceInfo for constrained AutoType static ConceptReference *createTrivialConceptReference(ASTContext &Context, diff --git a/clang/lib/AST/TypePrinter.cpp b/clang/lib/AST/TypePrinter.cpp index 075c8aba11fcb..6db6f58e4bd92 100644 --- a/clang/lib/AST/TypePrinter.cpp +++ b/clang/lib/AST/TypePrinter.cpp @@ -1,5 +1,7 @@ //===- TypePrinter.cpp - Pretty-Print Clang Types -------------------------===// // +// Copyright 2024 Bloomberg Finance L.P. +// // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception @@ -228,6 +230,7 @@ bool TypePrinter::canPrefixQualifiers(const Type *T, case Type::TypeOfExpr: case Type::TypeOf: case Type::Decltype: + case Type::ReflectionSplice: case Type::UnaryTransform: case Type::Record: case Type::Enum: @@ -1225,6 +1228,22 @@ void TypePrinter::printPackIndexingAfter(const PackIndexingType *T, void TypePrinter::printDecltypeAfter(const DecltypeType *T, raw_ostream &OS) {} +void TypePrinter::printReflectionSpliceBefore(const ReflectionSpliceType *T, + raw_ostream &OS) { + if (T->isDependentType()) { + OS << "typename [:"; + if (T->getOperand()) + T->getOperand()->printPretty(OS, nullptr, Policy); + OS << ":]"; + } else { + print(T->getUnderlyingType(), OS, StringRef()); + } + spaceBeforePlaceHolder(OS); +} + +void TypePrinter::printReflectionSpliceAfter(const ReflectionSpliceType *T, + raw_ostream &OS) { } + void TypePrinter::printUnaryTransformBefore(const UnaryTransformType *T, raw_ostream &OS) { IncludeStrongLifetimeRAII Strong(Policy); diff --git a/clang/lib/Basic/IdentifierTable.cpp b/clang/lib/Basic/IdentifierTable.cpp index a9b07aca65c05..2a1744715fcee 100644 --- a/clang/lib/Basic/IdentifierTable.cpp +++ b/clang/lib/Basic/IdentifierTable.cpp @@ -1,5 +1,7 @@ //===- IdentifierTable.cpp - Hash table for identifier lookup -------------===// // +// Copyright 2024 Bloomberg Finance L.P. +// // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception @@ -109,7 +111,8 @@ namespace { KEYCUDA = 0x1000000, KEYHLSL = 0x2000000, KEYFIXEDPOINT = 0x4000000, - KEYMAX = KEYFIXEDPOINT, // The maximum key + KEYREFLECT = 0x8000000, + KEYMAX = KEYREFLECT, // The maximum key KEYALLCXX = KEYCXX | KEYCXX11 | KEYCXX20, KEYALL = (KEYMAX | (KEYMAX-1)) & ~KEYNOMS18 & ~KEYNOOPENCL // KEYNOMS18 and KEYNOOPENCL are used to exclude. @@ -213,6 +216,8 @@ static KeywordStatus getKeywordStatusHelper(const LangOptions &LangOpts, return KS_Unknown; case KEYFIXEDPOINT: return LangOpts.FixedPoint ? KS_Enabled : KS_Disabled; + case KEYREFLECT: + return LangOpts.Reflection ? KS_Extension : KS_Unknown; default: llvm_unreachable("Unknown KeywordStatus flag"); } diff --git a/clang/lib/Basic/TargetInfo.cpp b/clang/lib/Basic/TargetInfo.cpp index f96956f31d50d..f05eb33ffe9b5 100644 --- a/clang/lib/Basic/TargetInfo.cpp +++ b/clang/lib/Basic/TargetInfo.cpp @@ -117,6 +117,8 @@ TargetInfo::TargetInfo(const llvm::Triple &T) : Triple(T) { LongDoubleAlign = 64; Float128Align = 128; Ibm128Align = 128; + MetaInfoWidth = 128; + MetaInfoAlign = 128; LargeArrayMinWidth = 0; LargeArrayAlign = 0; MaxAtomicPromoteWidth = MaxAtomicInlineWidth = 0; diff --git a/clang/lib/CodeGen/CGDebugInfo.cpp b/clang/lib/CodeGen/CGDebugInfo.cpp index 8c284c332171a..05b8a6415a5db 100644 --- a/clang/lib/CodeGen/CGDebugInfo.cpp +++ b/clang/lib/CodeGen/CGDebugInfo.cpp @@ -876,6 +876,7 @@ llvm::DIType *CGDebugInfo::CreateType(const BuiltinType *BT) { case BuiltinType::ULong: case BuiltinType::WChar_U: case BuiltinType::ULongLong: + case BuiltinType::MetaInfo: Encoding = llvm::dwarf::DW_ATE_unsigned; break; case BuiltinType::Short: @@ -2139,6 +2140,17 @@ CGDebugInfo::CollectTemplateParams(std::optional OArgs, TheCU, Name, TTy, defaultParameter, llvm::ConstantInt::get(CGM.getLLVMContext(), TA.getAsIntegral()))); } break; + case TemplateArgument::Reflection: { + TemplateParams.push_back( + DBuilder.createTemplateValueParameter( + TheCU, Name, + getOrCreateType(CGM.getContext().MetaInfoTy, Unit), + defaultParameter, + llvm::ConstantInt::get( + llvm::IntegerType::get(CGM.getLLVMContext(), sizeof(uintptr_t)), + reinterpret_cast( + TA.getAsReflection().getOpaqueValue())))); + } break; case TemplateArgument::Declaration: { const ValueDecl *D = TA.getAsDecl(); QualType T = TA.getParamTypeForDecl().getDesugaredType(CGM.getContext()); @@ -2240,6 +2252,7 @@ CGDebugInfo::CollectTemplateParams(std::optional OArgs, // And the following should never occur: case TemplateArgument::TemplateExpansion: case TemplateArgument::Null: + case TemplateArgument::IndeterminateSplice: llvm_unreachable( "These argument types shouldn't exist in concrete types"); } @@ -3455,6 +3468,9 @@ static QualType UnwrapTypeForDebugInfo(QualType T, const ASTContext &C) { case Type::Decltype: T = cast(T)->getUnderlyingType(); break; + case Type::ReflectionSplice: + T = cast(T)->getUnderlyingType(); + break; case Type::UnaryTransform: T = cast(T)->getUnderlyingType(); break; @@ -3678,6 +3694,7 @@ llvm::DIType *CGDebugInfo::CreateTypeNode(QualType Ty, llvm::DIFile *Unit) { case Type::TypeOfExpr: case Type::TypeOf: case Type::Decltype: + case Type::ReflectionSplice: case Type::PackIndexing: case Type::UnaryTransform: break; @@ -5422,6 +5439,7 @@ std::string CGDebugInfo::GetName(const Decl *D, bool Qualified) const { // feasible some day. return TA.getAsIntegral().getBitWidth() <= 64 && IsReconstitutableType(TA.getIntegralType()); + case TemplateArgument::Reflection: case TemplateArgument::StructuralValue: return false; case TemplateArgument::Type: diff --git a/clang/lib/CodeGen/CGDecl.cpp b/clang/lib/CodeGen/CGDecl.cpp index 267f2e40a7bba..674414d398f3f 100644 --- a/clang/lib/CodeGen/CGDecl.cpp +++ b/clang/lib/CodeGen/CGDecl.cpp @@ -1,5 +1,7 @@ //===--- CGDecl.cpp - Emit LLVM Code for declarations ---------------------===// // +// Copyright 2024 Bloomberg Finance L.P. +// // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception @@ -31,6 +33,7 @@ #include "clang/Basic/SourceManager.h" #include "clang/Basic/TargetInfo.h" #include "clang/CodeGen/CGFunctionInfo.h" +#include "clang/Frontend/FrontendDiagnostic.h" #include "clang/Sema/Sema.h" #include "llvm/Analysis/ValueTracking.h" #include "llvm/IR/DataLayout.h" @@ -51,6 +54,7 @@ void CodeGenFunction::EmitDecl(const Decl &D) { case Decl::TranslationUnit: case Decl::ExternCContext: case Decl::Namespace: + case Decl::DependentNamespace: case Decl::UnresolvedUsingTypename: case Decl::ClassTemplateSpecialization: case Decl::ClassTemplatePartialSpecialization: @@ -159,6 +163,14 @@ void CodeGenFunction::EmitDecl(const Decl &D) { const VarDecl &VD = cast(D); assert(VD.isLocalVarDecl() && "Should not see file-scope variables inside a function!"); + + // Meta types have no runtime meaning. + if (VD.getType()->isMetaType()) { + if (!VD.isConstexpr()) + CGM.getDiags().Report(VD.getLocation(), diag::err_runtime_meta_info); + return; + } + EmitVarDecl(VD); if (auto *DD = dyn_cast(&VD)) for (auto *B : DD->bindings()) diff --git a/clang/lib/CodeGen/CGExpr.cpp b/clang/lib/CodeGen/CGExpr.cpp index 2480972f1432f..d1b080bd233e2 100644 --- a/clang/lib/CodeGen/CGExpr.cpp +++ b/clang/lib/CodeGen/CGExpr.cpp @@ -1660,6 +1660,8 @@ LValue CodeGenFunction::EmitLValueHelper(const Expr *E, return EmitCoawaitLValue(cast(E)); case Expr::CoyieldExprClass: return EmitCoyieldLValue(cast(E)); + case Expr::CXXExprSpliceExprClass: + return EmitLValue(cast(E)->getOperand()); case Expr::PackIndexingExprClass: return EmitLValue(cast(E)->getSelectedExpr()); } diff --git a/clang/lib/CodeGen/CGExprAgg.cpp b/clang/lib/CodeGen/CGExprAgg.cpp index 1b9287ea23934..a3dc9b3deb2e2 100644 --- a/clang/lib/CodeGen/CGExprAgg.cpp +++ b/clang/lib/CodeGen/CGExprAgg.cpp @@ -242,6 +242,10 @@ class AggExprEmitter : public StmtVisitor { void VisitPackIndexingExpr(PackIndexingExpr *E) { Visit(E->getSelectedExpr()); } + + void VisitCXXExprSpliceExpr(CXXExprSpliceExpr *E) { + Visit(E->getOperand()); + } }; } // end anonymous namespace. diff --git a/clang/lib/CodeGen/CGExprConstant.cpp b/clang/lib/CodeGen/CGExprConstant.cpp index 9f1b06eebf9ed..a725de785ab61 100644 --- a/clang/lib/CodeGen/CGExprConstant.cpp +++ b/clang/lib/CodeGen/CGExprConstant.cpp @@ -1,5 +1,7 @@ //===--- CGExprConstant.cpp - Emit LLVM Code from Constant Expressions ----===// // +// Copyright 2024 Bloomberg Finance L.P. +// // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception @@ -2113,6 +2115,13 @@ llvm::Constant *ConstantEmitter::tryEmitPrivate(const APValue &Value, llvm::StructType::get(Complex[0]->getType(), Complex[1]->getType()); return llvm::ConstantStruct::get(STy, Complex); } + case APValue::Reflection: { + // FIXME: This emits an unused garbage value, but there's not much + // meaningful we can emit here. This seems okay, as the value only + // seems to be used in debug builds...But perhaps we can do better? + return llvm::ConstantInt::get(CGM.getLLVMContext(), + llvm::APInt(/*numBits=*/1, /*val=*/1)); + } case APValue::Float: { const llvm::APFloat &Init = Value.getFloat(); if (&Init.getSemantics() == &llvm::APFloat::IEEEhalf() && @@ -2248,7 +2257,9 @@ llvm::Constant * CodeGenModule::getMemberPointerConstant(const UnaryOperator *uo) { // Member pointer constants always have a very particular form. const MemberPointerType *type = cast(uo->getType()); - const ValueDecl *decl = cast(uo->getSubExpr())->getDecl(); + + const Expr *SubExpr = uo->getSubExpr()->IgnoreExprSplices(); + const ValueDecl *decl = cast(SubExpr)->getDecl(); // A member function pointer. if (const CXXMethodDecl *method = dyn_cast(decl)) diff --git a/clang/lib/CodeGen/CGExprScalar.cpp b/clang/lib/CodeGen/CGExprScalar.cpp index 397b4977acc3e..fe43185881a1e 100644 --- a/clang/lib/CodeGen/CGExprScalar.cpp +++ b/clang/lib/CodeGen/CGExprScalar.cpp @@ -1,5 +1,7 @@ //===--- CGExprScalar.cpp - Emit LLVM Code for Scalar Exprs ---------------===// // +// Copyright 2024 Bloomberg Finance L.P. +// // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception @@ -450,6 +452,9 @@ class ScalarExprEmitter Value *VisitUnaryCoawait(const UnaryOperator *E) { return Visit(E->getSubExpr()); } + Value *VisitCXXExprSpliceExpr(const CXXExprSpliceExpr *E) { + return Visit(E->getOperand()); + } // Leaves. Value *VisitIntegerLiteral(const IntegerLiteral *E) { @@ -1636,9 +1641,15 @@ void ScalarExprEmitter::EmitBinOpCheck( //===----------------------------------------------------------------------===// Value *ScalarExprEmitter::VisitExpr(Expr *E) { - CGF.ErrorUnsupported(E, "scalar expression"); - if (E->getType()->isVoidType()) - return nullptr; + if (E->getType()->isMetaType()) { + assert(!E->isIntegerConstantExpr(CGF.getContext()) && + "constexpr expressions should not reach here"); + CGF.ErrorNonConstexprMetaType(E); + } else { + CGF.ErrorUnsupported(E, "scalar expression"); + if (E->getType()->isVoidType()) + return nullptr; + } return llvm::UndefValue::get(CGF.ConvertType(E->getType())); } diff --git a/clang/lib/CodeGen/CodeGenFunction.cpp b/clang/lib/CodeGen/CodeGenFunction.cpp index 6474d6c8c1d1e..8d67d1fe29147 100644 --- a/clang/lib/CodeGen/CodeGenFunction.cpp +++ b/clang/lib/CodeGen/CodeGenFunction.cpp @@ -2009,6 +2009,13 @@ void CodeGenFunction::ErrorUnsupported(const Stmt *S, const char *Type) { CGM.ErrorUnsupported(S, Type); } +/// ErrorNonConstexprMetaType - Print out an error that meta type values must be +/// constexpr. +void CodeGenFunction::ErrorNonConstexprMetaType(const Stmt *S) { + CGM.Error(S->getBeginLoc(), + "values of meta types may only appear in constexpr contexts"); +} + /// emitNonZeroVLAInit - Emit the "zero" initialization of a /// variable-length array whose elements have a non-zero bit-pattern. /// @@ -2431,6 +2438,7 @@ void CodeGenFunction::EmitVariablyModifiedType(QualType type) { case Type::Typedef: case Type::Decltype: + case Type::ReflectionSplice: case Type::Auto: case Type::DeducedTemplateSpecialization: case Type::PackIndexing: diff --git a/clang/lib/CodeGen/CodeGenFunction.h b/clang/lib/CodeGen/CodeGenFunction.h index e2a7e28c8211e..0b874bd63e2c2 100644 --- a/clang/lib/CodeGen/CodeGenFunction.h +++ b/clang/lib/CodeGen/CodeGenFunction.h @@ -2599,6 +2599,10 @@ class CodeGenFunction : public CodeGenTypeCache { /// specified stmt yet. void ErrorUnsupported(const Stmt *S, const char *Type); + /// ErrorNonConstexprMeta - Print out an error that meta type values must be + /// constexpr. + void ErrorNonConstexprMetaType(const Stmt *S); + //===--------------------------------------------------------------------===// // Helpers //===--------------------------------------------------------------------===// diff --git a/clang/lib/CodeGen/CodeGenModule.cpp b/clang/lib/CodeGen/CodeGenModule.cpp index 00b3bfcaa0bc2..cedb7b43bda8a 100644 --- a/clang/lib/CodeGen/CodeGenModule.cpp +++ b/clang/lib/CodeGen/CodeGenModule.cpp @@ -4015,6 +4015,11 @@ void CodeGenModule::EmitMultiVersionFunctionDefinition(GlobalDecl GD, void CodeGenModule::EmitGlobalDefinition(GlobalDecl GD, llvm::GlobalValue *GV) { const auto *D = cast(GD.getDecl()); + if (D->getType()->isMetaType()) { + getDiags().Report(D->getLocation(), diag::err_runtime_meta_info); + return; + } + PrettyStackTraceDecl CrashInfo(const_cast(D), D->getLocation(), Context.getSourceManager(), "Generating code for declaration"); diff --git a/clang/lib/CodeGen/CodeGenTypes.cpp b/clang/lib/CodeGen/CodeGenTypes.cpp index 1568b6e6275b9..18bb233f3f8f1 100644 --- a/clang/lib/CodeGen/CodeGenTypes.cpp +++ b/clang/lib/CodeGen/CodeGenTypes.cpp @@ -425,6 +425,10 @@ llvm::Type *CodeGenTypes::ConvertType(QualType T) { ResultType = llvm::PointerType::getUnqual(getLLVMContext()); break; + case BuiltinType::MetaInfo: + ResultType = llvm::IntegerType::get(getLLVMContext(), 128); + break; + case BuiltinType::UInt128: case BuiltinType::Int128: ResultType = llvm::IntegerType::get(getLLVMContext(), 128); diff --git a/clang/lib/CodeGen/ItaniumCXXABI.cpp b/clang/lib/CodeGen/ItaniumCXXABI.cpp index 18acf7784f714..f991b0583e0a2 100644 --- a/clang/lib/CodeGen/ItaniumCXXABI.cpp +++ b/clang/lib/CodeGen/ItaniumCXXABI.cpp @@ -3315,6 +3315,7 @@ static bool TypeInfoIsInStandardLibrary(const BuiltinType *Ty) { switch (Ty->getKind()) { case BuiltinType::Void: case BuiltinType::NullPtr: + case BuiltinType::MetaInfo: case BuiltinType::Bool: case BuiltinType::WChar_S: case BuiltinType::WChar_U: diff --git a/clang/lib/Driver/ToolChains/Clang.cpp b/clang/lib/Driver/ToolChains/Clang.cpp index 766a9b91e3c0a..34870b6e0bdca 100644 --- a/clang/lib/Driver/ToolChains/Clang.cpp +++ b/clang/lib/Driver/ToolChains/Clang.cpp @@ -1,5 +1,7 @@ //===-- Clang.cpp - Clang+LLVM ToolChain Implementations --------*- C++ -*-===// // +// Copyright 2024 Bloomberg Finance L.P. +// // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception @@ -6922,8 +6924,13 @@ void Clang::ConstructJob(Compilation &C, const JobAction &JA, Args.AddLastArg(CmdArgs, options::OPT_fapinotes_swift_version); // -fblocks=0 is default. + // + // Suppress default-enablement of '-fblocks' if reflection is enabled, as + // '-fblocks' is incompatible with '-freflection'. + bool BlocksDefault = TC.IsBlocksDefault() && + !Args.hasArg(options::OPT_freflection); if (Args.hasFlag(options::OPT_fblocks, options::OPT_fno_blocks, - TC.IsBlocksDefault()) || + BlocksDefault) || (Args.hasArg(options::OPT_fgnu_runtime) && Args.hasArg(options::OPT_fobjc_nonfragile_abi) && !Args.hasArg(options::OPT_fno_blocks))) { @@ -7238,6 +7245,13 @@ void Clang::ConstructJob(Compilation &C, const JobAction &JA, Args.addOptInFlag(CmdArgs, options::OPT_frelaxed_template_template_args, options::OPT_fno_relaxed_template_template_args); + // -freflection is off by default, as it is experimental. + Args.addOptInFlag(CmdArgs, options::OPT_freflection, + options::OPT_fno_reflection); + // -fparameter-reflection is likewise off by default. + Args.addOptInFlag(CmdArgs, options::OPT_fparameter_reflection, + options::OPT_fno_parameter_reflection); + // -fsized-deallocation is off by default, as it is an ABI-breaking change for // most platforms. Args.addOptInFlag(CmdArgs, options::OPT_fsized_deallocation, diff --git a/clang/lib/Frontend/CompilerInvocation.cpp b/clang/lib/Frontend/CompilerInvocation.cpp index 1f1f5440ddd75..097bf61407d2b 100644 --- a/clang/lib/Frontend/CompilerInvocation.cpp +++ b/clang/lib/Frontend/CompilerInvocation.cpp @@ -1,5 +1,7 @@ //===- CompilerInvocation.cpp ---------------------------------------------===// // +// Copyright 2024 Bloomberg Finance L.P. +// // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception @@ -610,6 +612,13 @@ static bool FixupInvocation(CompilerInvocation &Invocation, LangOpts.NewAlignOverride = 0; } + if (LangOpts.Reflection) { + if (Args.hasArg(OPT_fblocks)) + Diags.Report(diag::err_fe_reflection_incompatible_with_blocks); + } else if (LangOpts.ParameterReflection) { + Diags.Report(diag::err_fe_parameter_reflection_without_reflection); + } + // Prevent the user from specifying both -fsycl-is-device and -fsycl-is-host. if (LangOpts.SYCLIsDevice && LangOpts.SYCLIsHost) Diags.Report(diag::err_drv_argument_not_allowed_with) << "-fsycl-is-device" diff --git a/clang/lib/Index/IndexTypeSourceInfo.cpp b/clang/lib/Index/IndexTypeSourceInfo.cpp index b986ccde57452..eac3b69f69c5c 100644 --- a/clang/lib/Index/IndexTypeSourceInfo.cpp +++ b/clang/lib/Index/IndexTypeSourceInfo.cpp @@ -283,6 +283,7 @@ void IndexingContext::indexNestedNameSpecifierLoc(NestedNameSpecifierLoc NNS, case NestedNameSpecifier::Identifier: case NestedNameSpecifier::Global: case NestedNameSpecifier::Super: + case NestedNameSpecifier::IndeterminateSplice: break; case NestedNameSpecifier::Namespace: diff --git a/clang/lib/Index/USRGeneration.cpp b/clang/lib/Index/USRGeneration.cpp index 5acc86191f8f9..961a378943405 100644 --- a/clang/lib/Index/USRGeneration.cpp +++ b/clang/lib/Index/USRGeneration.cpp @@ -1,5 +1,7 @@ //===- USRGeneration.cpp - Routines for USR generation --------------------===// // +// Copyright 2024 Bloomberg Finance L.P. +// // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception @@ -737,6 +739,8 @@ void USRGenerator::VisitType(QualType T) { Out << 'Q'; break; case BuiltinType::NullPtr: Out << 'n'; break; + case BuiltinType::MetaInfo: + Out << 'm'; break; #define IMAGE_TYPE(ImgType, Id, SingletonId, Access, Suffix) \ case BuiltinType::Id: \ Out << "@BT@" << #Suffix << "_" << #ImgType; break; @@ -1053,6 +1057,12 @@ void USRGenerator::VisitTemplateArgument(const TemplateArgument &Arg) { Out << Arg.getAsIntegral(); break; + case TemplateArgument::Reflection: + Out << 'R'; + VisitType(Arg.getReflectionType()); + Out << "(reflection)"; + break; + case TemplateArgument::StructuralValue: { Out << 'S'; VisitType(Arg.getStructuralValueType()); diff --git a/clang/lib/Lex/Lexer.cpp b/clang/lib/Lex/Lexer.cpp index c98645993abe0..9e91183f1e617 100644 --- a/clang/lib/Lex/Lexer.cpp +++ b/clang/lib/Lex/Lexer.cpp @@ -1,5 +1,7 @@ //===- Lexer.cpp - C Language Family Lexer --------------------------------===// // +// Copyright 2024 Bloomberg Finance L.P. +// // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception @@ -4021,9 +4023,39 @@ bool Lexer::LexTokenInternal(Token &Result, bool TokAtPhysicalStartOfLine) { case '?': Kind = tok::question; break; - case '[': - Kind = tok::l_square; + case '[': { + size_t SuccessiveColons = 0; + + // There may be as many as 3 successive colons: + // - `[: ...` for an 'l_splice' token. + // - `[:: ...` for an 'l_square' token followed by a 'coloncolon' token. + // - `[: :: ...` for an 'l_splice' token followed by a 'coloncolon' token. + Char = getCharAndSize(CurPtr, SizeTmp); + SizeTmp2 = 0; + while (Char == ':' && SuccessiveColons <= 3) { + unsigned SizeTmp3; + Char = getCharAndSize(CurPtr + SizeTmp + SizeTmp2, SizeTmp3); + SizeTmp2 += SizeTmp3; + + if (Char != '>') // Check for ':>'-digraph. + ++SuccessiveColons; + } + + // Every `[:`-pattern except for `[::` indicates an `l_splice` token. + if (SuccessiveColons > 0 && SuccessiveColons != 2) { + if (LangOpts.Reflection) { + Kind = tok::l_splice; + CurPtr += SizeTmp; + } else { + if (!isLexingRawMode() && !LangOpts.OpenMP) + Diag(CurPtr, diag::warn_reflection_disabled) << "[:"; + Kind = tok::l_square; + } + } else { + Kind = tok::l_square; + } break; + } case ']': Kind = tok::r_square; break; @@ -4344,6 +4376,15 @@ bool Lexer::LexTokenInternal(Token &Result, bool TokAtPhysicalStartOfLine) { } else if (Char == ':') { Kind = tok::coloncolon; CurPtr = ConsumeChar(CurPtr, SizeTmp, Result); + } else if (Char == ']') { + if (LangOpts.Reflection) { + Kind = tok::r_splice; + CurPtr = ConsumeChar(CurPtr, SizeTmp, Result); + } else { + if (!isLexingRawMode() && !LangOpts.OpenMP) + Diag(BufferPtr, diag::warn_reflection_disabled) << ":]"; + Kind = tok::colon; + } } else { Kind = tok::colon; } diff --git a/clang/lib/Parse/CMakeLists.txt b/clang/lib/Parse/CMakeLists.txt index 22e902f7e1bc5..faa55d3f10913 100644 --- a/clang/lib/Parse/CMakeLists.txt +++ b/clang/lib/Parse/CMakeLists.txt @@ -18,6 +18,7 @@ add_clang_library(clangParse ParseObjc.cpp ParseOpenMP.cpp ParsePragma.cpp + ParseReflect.cpp ParseStmt.cpp ParseStmtAsm.cpp ParseTemplate.cpp diff --git a/clang/lib/Parse/ParseDecl.cpp b/clang/lib/Parse/ParseDecl.cpp index 0aa14b0510746..8ced52c8ba9c7 100644 --- a/clang/lib/Parse/ParseDecl.cpp +++ b/clang/lib/Parse/ParseDecl.cpp @@ -1,5 +1,7 @@ //===--- ParseDecl.cpp - Declaration Parsing --------------------*- C++ -*-===// // +// Copyright 2024 Bloomberg Finance L.P. +// // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception @@ -3158,6 +3160,8 @@ Parser::getDeclSpecContextFromDeclaratorContext(DeclaratorContext Context) { return DeclSpecContext::DSC_conv_operator; case DeclaratorContext::CXXNew: return DeclSpecContext::DSC_new; + case DeclaratorContext::ReflectOperator: + return DeclSpecContext::DSC_reflect_operator; case DeclaratorContext::Prototype: case DeclaratorContext::ObjCResult: case DeclaratorContext::ObjCParameter: @@ -3573,6 +3577,28 @@ void Parser::ParseDeclarationSpecifiers( DS.Finish(Actions, Policy); return; + case tok::l_splice: + if (TryAnnotateTypeOrScopeToken(AllowImplicitTypename)) { + DS.SetTypeSpecError(); + break; + } + continue; + + case tok::annot_splice: { + ExprResult Result = getExprAnnotation(Tok); + assert(!Result.isInvalid()); + + if (DS.SetTypeSpecType(DeclSpec::TST_type_splice, Tok.getLocation(), + PrevSpec, DiagID, Result.get(), Policy)) { + Diag(Tok.getLocation(), DiagID) << PrevSpec; + DS.SetTypeSpecError(); + break; + } + DS.SetRangeEnd(Tok.getAnnotationEndLoc()); + ConsumeAnnotationToken(); + continue; + } + // alignment-specifier case tok::kw__Alignas: diagnoseUseOfC11Keyword(Tok); @@ -6455,7 +6481,8 @@ void Parser::ParseDeclaratorInternal(Declarator &D, (Tok.is(tok::coloncolon) || Tok.is(tok::kw_decltype) || (Tok.is(tok::identifier) && (NextToken().is(tok::coloncolon) || NextToken().is(tok::less))) || - Tok.is(tok::annot_cxxscope))) { + Tok.is(tok::annot_cxxscope) || Tok.is(tok::l_splice) || + Tok.is(tok::annot_splice))) { bool EnteringContext = D.getContext() == DeclaratorContext::File || D.getContext() == DeclaratorContext::Member; CXXScopeSpec SS; @@ -6828,7 +6855,8 @@ void Parser::ParseDirectDeclarator(Declarator &D) { // An identifier within parens is unlikely to be intended to be anything // other than a name being "declared". DiagnoseIdentifier = true; - else if (D.getContext() == DeclaratorContext::TemplateArg) + else if (D.getContext() == DeclaratorContext::TemplateArg || + D.getContext() == DeclaratorContext::ReflectOperator) // T is an accidental identifier; T'. DiagnoseIdentifier = NextToken().isOneOf(tok::comma, tok::greater, tok::greatergreater); diff --git a/clang/lib/Parse/ParseDeclCXX.cpp b/clang/lib/Parse/ParseDeclCXX.cpp index 861a25dc5103c..53310b19be887 100644 --- a/clang/lib/Parse/ParseDeclCXX.cpp +++ b/clang/lib/Parse/ParseDeclCXX.cpp @@ -1,5 +1,7 @@ //===--- ParseDeclCXX.cpp - C++ Declaration Parsing -------------*- C++ -*-===// // +// Copyright 2024 Bloomberg Finance L.P. +// // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception @@ -257,6 +259,32 @@ Parser::DeclGroupPtrTy Parser::ParseNamespace(DeclaratorContext Context, ImplicitUsingDirectiveDecl); } +/// ParseNamespaceName - Parse the name of a namespace. +Decl *Parser::ParseNamespaceName(CXXScopeSpec &SS, SourceLocation &IdentLoc) { + // Will be one of: + // - A nested-names-specifier followed by an identifier, or + // - An unqualified splice (C++2c, P2996). + + ParseOptionalCXXScopeSpecifier(SS, /*ObjectType=*/nullptr, + /*ObjectHadErrors=*/false, + /*EnteringContext=*/false, + /*MayBePseudoDestructor=*/nullptr, + /*IsTypename=*/false, + /*LastII=*/nullptr, + /*OnlyNamespace=*/false); + + if (Tok.is(tok::identifier)) { + IdentifierInfo *II = Tok.getIdentifierInfo(); + IdentLoc = ConsumeToken(); + return Actions.ActOnNamespaceName(getCurScope(), SS, II, IdentLoc); + } else if (SS.isValid() && + SS.getScopeRep()->getKind() == NestedNameSpecifier::Global) { + return Actions.getASTContext().getTranslationUnitDecl(); + } + + return nullptr; +} + /// ParseInnerNamespace - Parse the contents of a namespace. void Parser::ParseInnerNamespace(const InnerNamespaceInfoList &InnerNSs, unsigned int index, SourceLocation &InlineLoc, @@ -323,7 +351,24 @@ Decl *Parser::ParseNamespaceAlias(SourceLocation NamespaceLoc, /*LastII=*/nullptr, /*OnlyNamespace=*/true); - if (Tok.isNot(tok::identifier)) { + // Check for a splice expression without a leading nested-name-specififer. + if (Tok.is(tok::annot_splice) && SS.isEmpty()) { + SourceLocation NSLoc = Tok.getLocation(); + + DeclResult DR = ParseCXXSpliceAsNamespace(); + if (DR.isInvalid()) + return nullptr; + + // Eat the ';'. + DeclEnd = Tok.getLocation(); + if (ExpectAndConsume(tok::semi, + diag::err_expected_semi_after_namespace_name)) + SkipUntil(tok::semi); + + return Actions.ActOnNamespaceAliasDef(getCurScope(), NamespaceLoc, AliasLoc, + Alias, SS, NSLoc, + cast(DR.get())); + } else if (Tok.isNot(tok::identifier)) { Diag(Tok, diag::err_expected_namespace_name); // Skip to end of the definition and eat the ';'. SkipUntil(tok::semi); @@ -559,8 +604,40 @@ Decl *Parser::ParseUsingDirective(DeclaratorContext Context, IdentifierInfo *NamespcName = nullptr; SourceLocation IdentLoc = SourceLocation(); - // Parse namespace-name. - if (Tok.isNot(tok::identifier)) { + // Check for a splice expression without a leading nested-name-specififer. + if (Tok.is(tok::annot_splice) && SS.isEmpty()) { + SourceLocation NSLoc = Tok.getLocation(); + + DeclResult DR = ParseCXXSpliceAsNamespace(); + if (DR.isInvalid()) + return nullptr; + + NamedDecl *Named = cast(DR.get()); + NamespaceDecl *NS = nullptr; + if (auto *Alias = dyn_cast(Named)) + NS = Alias->getNamespace(); + else + NS = cast(Named); + + // Parse (optional) attributes (most likely GNU strong-using extension). + bool GNUAttr = false; + if (Tok.is(tok::kw___attribute)) { + GNUAttr = true; + ParseGNUAttributes(attrs); + } + + // Eat ';'. + DeclEnd = Tok.getLocation(); + if (ExpectAndConsume( + tok::semi, + GNUAttr ? diag::err_expected_semi_after_attribute_list + : diag::err_expected_semi_after_namespace_name)) + SkipUntil(tok::semi); + + return Actions.ActOnUsingDirective(getCurScope(), UsingLoc, NamespcLoc, + SS, NSLoc, Named, NS, attrs); + } else if (Tok.isNot(tok::identifier)) { + // Parse namespace-name. Diag(Tok, diag::err_expected_namespace_name); // If there was invalid namespace name, skip to end of decl, and eat ';'. SkipUntil(tok::semi); @@ -737,6 +814,32 @@ Parser::DeclGroupPtrTy Parser::ParseUsingDeclaration( return nullptr; } + if (Tok.is(tok::annot_splice)) { + SourceLocation SpliceLoc = Tok.getLocation(); + TypeResult TR = ParseCXXSpliceAsType(/*AllowDependent=*/true, + /*Complain=*/true); + if (TR.isInvalid()) { + SkipUntil(tok::semi); + return nullptr; + } + TypeSourceInfo *TSI; + QualType EnumTy = Actions.GetTypeFromParser(TR.get(), &TSI); + if (EnumTy.isNull()) { + SkipUntil(tok::semi); + return nullptr; + } + + Decl *UED = Actions.ActOnUsingEnumDeclaration(getCurScope(), AS, UsingLoc, + UELoc, SpliceLoc, EnumTy, + TSI); + if (!UED) { + SkipUntil(tok::semi); + return nullptr; + } + + return Actions.ConvertDeclToDeclGroup(UED); + } + if (!Tok.is(tok::identifier)) { Diag(Tok.getLocation(), diag::err_using_enum_expect_identifier) << Tok.is(tok::kw_enum); @@ -1413,6 +1516,9 @@ TypeResult Parser::ParseBaseTypeSpecifier(SourceLocation &BaseLoc, // Fall through to produce an error below. } + if (Tok.is(tok::annot_splice)) + return ParseCXXSpliceAsType(/*AllowDependent=*/true, /*Complain=*/true); + if (Tok.isNot(tok::identifier)) { Diag(Tok, diag::err_expected_class_name); return true; @@ -4802,14 +4908,15 @@ void Parser::ParseCXX11AttributeSpecifierInternal(ParsedAttributes &Attrs, CommonScopeLoc, Sema::AttributeCompletion::Scope); if (!CommonScopeName) { Diag(Tok.getLocation(), diag::err_expected) << tok::identifier; - SkipUntil(tok::r_square, tok::colon, StopBeforeMatch); + SkipUntil(tok::r_square, tok::colon, tok::r_splice, StopBeforeMatch); } - if (!TryConsumeToken(tok::colon) && CommonScopeName) + if (!TryConsumeToken(tok::colon) && !Tok.is(tok::r_splice) && + CommonScopeName) Diag(Tok.getLocation(), diag::err_expected) << tok::colon; } bool AttrParsed = false; - while (!Tok.isOneOf(tok::r_square, tok::semi, tok::eof)) { + while (!Tok.isOneOf(tok::r_square, tok::semi, tok::eof, tok::r_splice)) { if (AttrParsed) { // If we parsed an attribute, a comma is required before parsing any // additional attributes. @@ -4884,7 +4991,9 @@ void Parser::ParseCXX11AttributeSpecifierInternal(ParsedAttributes &Attrs, } SourceLocation CloseLoc = Tok.getLocation(); - if (ExpectAndConsume(tok::r_square)) + if (Tok.is(tok::r_splice)) + ConsumeToken(); + else if (ExpectAndConsume(tok::r_square)) SkipUntil(tok::r_square); else if (Tok.is(tok::r_square)) checkCompoundToken(CloseLoc, tok::r_square, CompoundToken::AttrEnd); diff --git a/clang/lib/Parse/ParseExpr.cpp b/clang/lib/Parse/ParseExpr.cpp index ae23cb432c439..b96a0508d39b5 100644 --- a/clang/lib/Parse/ParseExpr.cpp +++ b/clang/lib/Parse/ParseExpr.cpp @@ -1,5 +1,7 @@ //===--- ParseExpr.cpp - Expression Parsing -------------------------------===// // +// Copyright 2024 Bloomberg Finance L.P. +// // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception @@ -840,6 +842,7 @@ class CastExpressionIdValidator final : public CorrectionCandidateCallback { /// [G++] unary-type-trait '(' type-id ')' /// [G++] binary-type-trait '(' type-id ',' type-id ')' [TODO] /// [EMBT] array-type-trait '(' type-id ',' integer ')' +/// [C++2c] '^' reflect-operand /// [clang] '^' block-literal /// /// constant: [C99 6.4.4] @@ -1646,15 +1649,26 @@ ExprResult Parser::ParseCastExpression(CastParseKind ParseKind, break; } + case tok::annot_splice: { + // An 'annot_splice' was parsed by 'TryAnnotateTypeOrScopeToken', but it + // could not be spliced as a type; it must be an expression. + Res = ParseCXXSpliceAsExpr(/*AllowMemberReference=*/isAddressOfOperand); + break; + } + + case tok::l_splice: case tok::annot_cxxscope: { // [C++] id-expression: qualified-id // If TryAnnotateTypeOrScopeToken annotates the token, tail recurse. // (We can end up in this situation after tentative parsing.) - if (TryAnnotateTypeOrScopeToken()) + if (TryAnnotateTypeOrScopeToken()) { return ExprError(); - if (!Tok.is(tok::annot_cxxscope)) - return ParseCastExpression(ParseKind, isAddressOfOperand, NotCastExpr, + } + if (!Tok.is(tok::annot_cxxscope)) { + auto result = ParseCastExpression(ParseKind, isAddressOfOperand, NotCastExpr, isTypeCast, isVectorLiteral, NotPrimaryExpression); + return result; + } Token Next = NextToken(); if (Next.is(tok::annot_template_id)) { @@ -1796,6 +1810,9 @@ ExprResult Parser::ParseCastExpression(CastParseKind ParseKind, Res = ParseExpressionTrait(); break; + case tok::kw___metafunction: + return ParseCXXMetafunctionExpression(); + case tok::at: { if (NotPrimaryExpression) *NotPrimaryExpression = true; @@ -1803,7 +1820,12 @@ ExprResult Parser::ParseCastExpression(CastParseKind ParseKind, return ParseObjCAtExpression(AtLoc); } case tok::caret: - Res = ParseBlockLiteralExpression(); + // '-freflection' and '-fblocks' are mutually exclusive. + if (getLangOpts().Reflection) { + return ParseCXXReflectExpression(); + } else { + Res = ParseBlockLiteralExpression(); + } break; case tok::code_completion: { cutOffParsing(); @@ -2304,6 +2326,18 @@ Parser::ParsePostfixExpressionSuffix(ExprResult LHS) { IdentifierInfo *Id = Tok.getIdentifierInfo(); SourceLocation Loc = ConsumeToken(); Name.setIdentifier(Id, Loc); + } else if (Tok.is(tok::annot_splice)) { + ExprResult Res = ParseCXXSpliceAsExpr(/*AllowMemberReference=*/true); + if (!Res.isInvalid() && !Diags.hasErrorOccurred()) { + LHS = Actions.ActOnMemberAccessExpr( + getCurScope(), LHS.get(), OpLoc, OpKind, + dyn_cast(Res.get()), TemplateKWLoc); + if (!LHS.isInvalid() && Tok.is(tok::less)) + checkPotentialAngleBracket(LHS); + break; + } else { + LHS = ExprError(); + } } else if (ParseUnqualifiedId( SS, ObjectType, LHS.get() && LHS.get()->containsErrors(), /*EnteringContext=*/false, diff --git a/clang/lib/Parse/ParseExprCXX.cpp b/clang/lib/Parse/ParseExprCXX.cpp index 73c85c585baae..397f0db0d749d 100644 --- a/clang/lib/Parse/ParseExprCXX.cpp +++ b/clang/lib/Parse/ParseExprCXX.cpp @@ -1,5 +1,7 @@ //===--- ParseExprCXX.cpp - C++ Expression Parsing ------------------------===// // +// Copyright 2024 Bloomberg Finance L.P. +// // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception @@ -231,9 +233,29 @@ bool Parser::ParseOptionalCXXScopeSpecifier( SS.SetInvalid(SourceRange(DeclLoc, CCLoc)); HasScopeSpecifier = true; - } + } else if (!HasScopeSpecifier && Tok.is(tok::l_splice)) { + if (ParseCXXIndeterminateSplice()) { + return true; + } + + if (!NextToken().is(tok::coloncolon)) { + // This isn't a nested-name-specifier; leave the 'annot_splice' token. + return false; + } + ExprResult Result = getExprAnnotation(Tok); + ConsumeAnnotationToken(); - else if (!HasScopeSpecifier && Tok.is(tok::identifier) && + SourceLocation CCLoc; + TryConsumeToken(tok::coloncolon, CCLoc); + CXXIndeterminateSpliceExpr *SpliceExpr = + dyn_cast(Result.get()); + if (Actions.ActOnCXXNestedNameSpecifierReflectionSplice(SS, SpliceExpr, + CCLoc)) { + SS.SetInvalid(SourceRange(SpliceExpr->getExprLoc(), CCLoc)); + return true; + } + HasScopeSpecifier = true; + } else if (!HasScopeSpecifier && Tok.is(tok::identifier) && GetLookAheadToken(1).is(tok::ellipsis) && GetLookAheadToken(2).is(tok::l_square)) { SourceLocation Start = Tok.getLocation(); diff --git a/clang/lib/Parse/ParseReflect.cpp b/clang/lib/Parse/ParseReflect.cpp new file mode 100644 index 0000000000000..69e731383ef5d --- /dev/null +++ b/clang/lib/Parse/ParseReflect.cpp @@ -0,0 +1,235 @@ +//===--- ParseReflect.cpp - C++2c Reflection Parsing (P2996) --------------===// +// +// Copyright 2024 Bloomberg Finance L.P. +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This file implements parsing for reflection facilities. +// +//===----------------------------------------------------------------------===// + +#include "clang/Parse/ParseDiagnostic.h" +#include "clang/Parse/Parser.h" +#include "clang/Parse/RAIIObjectsForParser.h" +#include "clang/Sema/EnterExpressionEvaluationContext.h" +using namespace clang; + +ExprResult Parser::ParseCXXReflectExpression() { + assert(Tok.is(tok::caret) && "expected '^'"); + SourceLocation OpLoc = ConsumeToken(); + + // Handle case of '^[: ... :]' specially. + if (Tok.is(tok::l_splice)) { + // Try to parse the splice as a part of a nested-name-specifier. + CXXScopeSpec SS; + if (ParseOptionalCXXScopeSpecifier(SS, /*ObjectType=*/nullptr, + /*ObjectHasErrors=*/false, + /*EnteringContext=*/false)) + return ExprError(); + + // If 'SS' is empty but parsing did not fail, we should have an + // 'annot_splice'. This is a lone splice token without a leading qualifier. + if (SS.isEmpty()) { + assert(Tok.is(tok::annot_splice)); + + ExprResult ER = getExprAnnotation(Tok); + assert(!ER.isInvalid()); + ConsumeAnnotationToken(); + + return Actions.ActOnCXXReflectExpr( + OpLoc, cast(ER.get())); + } + + // Otherwise, insert 'SS' back into the token stream as an 'annot_scope', + // and continue to parse as usual. + AnnotateScopeToken(SS, true); + } + + // Try parsing as a template-template argument. + // This is probably "close enough" to "unparameterized template ID". + { + TentativeParsingAction TentativeAction(*this); + ParsedTemplateArgument Template; + { + EnterExpressionEvaluationContext EvalContext( + Actions, Sema::ExpressionEvaluationContext::ReflectionContext); + Template = ParseTemplateReflectOperand(); + } + if (!Template.isInvalid()) { + TentativeAction.Commit(); + return Actions.ActOnCXXReflectExpr(OpLoc, Template); + } + TentativeAction.Revert(); + } + + // Try parsing as a namespace name. + { + TentativeParsingAction TentativeAction(*this); + Decl *NSDecl; + { + EnterExpressionEvaluationContext EvalContext( + Actions, Sema::ExpressionEvaluationContext::ReflectionContext); + CXXScopeSpec SS; + SourceLocation IdLoc; + NSDecl = ParseNamespaceName(SS, IdLoc); + if (NSDecl) { + TentativeAction.Commit(); + return Actions.ActOnCXXReflectExpr(OpLoc, IdLoc, NSDecl); + } + } + TentativeAction.Revert(); + } + + // Try parsing as type-id. + if (isCXXTypeId(TypeIdAsReflectionOperand)) { + TypeResult TR; + { + EnterExpressionEvaluationContext EvalContext( + Actions, Sema::ExpressionEvaluationContext::ReflectionContext); + TR = ParseTypeName(nullptr, DeclaratorContext::ReflectOperator); + } + if (TR.isInvalid()) { + return ExprError(); + } + return Actions.ActOnCXXReflectExpr(OpLoc, TR); + } + + // Otherwise, parse as an expression. + ExprResult E; + { + EnterExpressionEvaluationContext EvalContext( + Actions, Sema::ExpressionEvaluationContext::ReflectionContext); + E = ParseCastExpression(AnyCastExpr, true); + } + if (E.isInvalid() || !E.get()) + return ExprError(); + + return Actions.ActOnCXXReflectExpr(OpLoc, E.get()); +} + +ExprResult Parser::ParseCXXMetafunctionExpression() { + assert(Tok.is(tok::kw___metafunction) && "expected '___metafunction'"); + SourceLocation KwLoc = ConsumeToken(); + + // Balance any number of arguments in parens. + BalancedDelimiterTracker Parens(*this, tok::l_paren); + if (Parens.expectAndConsume()) + return ExprError(); + + SmallVector Args; + do { + ExprResult Expr = ParseConstantExpression(); + if (Expr.isInvalid()) { + Parens.skipToEnd(); + return ExprError(); + } + Args.push_back(Expr.get()); + } while (TryConsumeToken(tok::comma)); + + if (Parens.consumeClose()) + return ExprError(); + + SourceLocation LPLoc = Parens.getOpenLocation(); + SourceLocation RPLoc = Parens.getCloseLocation(); + return Actions.ActOnCXXMetafunction(KwLoc, LPLoc, Args, RPLoc); +} + +bool Parser::ParseCXXIndeterminateSplice() { + assert(Tok.is(tok::l_splice) && "expected '[:'"); + + BalancedDelimiterTracker SpliceTokens(*this, tok::l_splice); + if (SpliceTokens.expectAndConsume()) + return true; + + ExprResult ER; + { + EnterExpressionEvaluationContext EvalContext( + Actions, Sema::ExpressionEvaluationContext::ConstantEvaluated); + ER = ParseConstantExpression(); + } + if (ER.isInvalid()) { + SpliceTokens.skipToEnd(); + return true; + } + Expr *Operand = ER.get(); + + Token end = Tok; + if (SpliceTokens.consumeClose()) + return true; + + SourceLocation LSplice = SpliceTokens.getOpenLocation(); + SourceLocation RSplice = SpliceTokens.getCloseLocation(); + + ER = Actions.ActOnCXXIndeterminateSpliceExpr(LSplice, Operand, RSplice); + if (ER.isInvalid() || ER.get()->containsErrors()) + return true; + Expr *SpliceExpr = ER.get(); + + UnconsumeToken(end); + Tok.setKind(tok::annot_splice); + setExprAnnotation(Tok, SpliceExpr); + Tok.setLocation(LSplice); + Tok.setAnnotationEndLoc(RSplice); + PP.AnnotateCachedTokens(Tok); + + return false; +} + +TypeResult Parser::ParseCXXSpliceAsType(bool AllowDependent, + bool Complain) { + assert(Tok.is(tok::annot_splice) && "expected annot_splice"); + + Token Splice = Tok; + + ExprResult ER = getExprAnnotation(Splice); + assert(!ER.isInvalid()); + Expr *Operand = ER.get(); + + if (!AllowDependent) + if (Operand->isTypeDependent() || Operand->isValueDependent()) + return TypeError(); + + TypeResult Result = Actions.ActOnCXXSpliceExpectingType( + Splice.getLocation(), ER.get(), Splice.getAnnotationEndLoc(), + Complain); + if (!Result.isInvalid()) + ConsumeAnnotationToken(); + + return Result; +} + +ExprResult Parser::ParseCXXSpliceAsExpr(bool AllowMemberReference) { + assert(Tok.is(tok::annot_splice) && "expected annot_splice"); + + Token Splice = Tok; + + ExprResult ER = getExprAnnotation(Splice); + assert(!ER.isInvalid()); + + ExprResult Result = Actions.ActOnCXXSpliceExpectingExpr( + Splice.getLocation(), ER.get(), Splice.getAnnotationEndLoc(), + AllowMemberReference); + if (!Result.isInvalid()) + ConsumeAnnotationToken(); + + return Result; +} + +DeclResult Parser::ParseCXXSpliceAsNamespace() { + assert(Tok.is(tok::annot_splice) && "expected annot_splice"); + + Token Splice = Tok; + ConsumeAnnotationToken(); + + ExprResult ER = getExprAnnotation(Splice); + assert(!ER.isInvalid()); + + DeclResult Result = Actions.ActOnCXXSpliceExpectingNamespace( + Splice.getLocation(), ER.get(), Splice.getAnnotationEndLoc()); + + return Result; +} diff --git a/clang/lib/Parse/ParseTemplate.cpp b/clang/lib/Parse/ParseTemplate.cpp index d4897f8f66072..eda2bd08170d8 100644 --- a/clang/lib/Parse/ParseTemplate.cpp +++ b/clang/lib/Parse/ParseTemplate.cpp @@ -1,5 +1,7 @@ //===--- ParseTemplate.cpp - Template Parsing -----------------------------===// // +// Copyright 2024 Bloomberg Finance L.P. +// // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception @@ -1374,6 +1376,15 @@ static bool isEndOfTemplateArgument(Token Tok) { tok::greatergreatergreater); } +/// Determine whether the given token can end a template reflection operand. +static bool isEndOfTemplateReflectOperand(Token Tok) { + // The set of tokens that can follow a reflection operand is far more general + // than the set of tokens that can conclude a template template argument. For + // now, just assume that -any- token can conclude such an operand. Revisit + // this if the parsing turns out to be more involved. + return true; +} + /// Parse a C++ template template argument. ParsedTemplateArgument Parser::ParseTemplateTemplateArgument() { if (!Tok.is(tok::identifier) && !Tok.is(tok::coloncolon) && @@ -1452,6 +1463,94 @@ ParsedTemplateArgument Parser::ParseTemplateTemplateArgument() { return Result; } +/// Parse a C++ template operand to a reflect expression (C++2c, P2996). +ParsedTemplateArgument Parser::ParseTemplateReflectOperand() { + if (!Tok.is(tok::identifier) && !Tok.is(tok::coloncolon) && + !Tok.is(tok::annot_cxxscope)) + return ParsedTemplateArgument(); + + CXXScopeSpec SS; // nested-name-specifier, if present + ParseOptionalCXXScopeSpecifier(SS, /*ObjectType=*/nullptr, + /*ObjectHasErrors=*/false, + /*EnteringContext=*/false); + + ParsedTemplateArgument Result; + SourceLocation EllipsisLoc; + if (SS.isSet() && Tok.is(tok::kw_template)) { + // Parse the optional 'template' keyword following the + // nested-name-specifier. + SourceLocation TemplateKWLoc = ConsumeToken(); + + if (Tok.is(tok::identifier)) { + // We appear to have a dependent template name. + UnqualifiedId Name; + Name.setIdentifier(Tok.getIdentifierInfo(), Tok.getLocation()); + ConsumeToken(); // the identifier + + TryConsumeToken(tok::ellipsis, EllipsisLoc); + + // If the next token signals the end of a template argument, then we have + // a (possibly-dependent) template name that could be a reflect expression + // operand. + TemplateTy Template; + if (isEndOfTemplateReflectOperand(Tok) && + Actions.ActOnTemplateName(getCurScope(), SS, TemplateKWLoc, Name, + /*ObjectType=*/nullptr, + /*EnteringContext=*/false, Template)) + Result = ParsedTemplateArgument(SS, Template, Name.StartLocation); + } + } else if (Tok.is(tok::identifier)) { + // We may have a (non-dependent) template name. + TemplateTy Template; + UnqualifiedId Name; + Name.setIdentifier(Tok.getIdentifierInfo(), Tok.getLocation()); + ConsumeToken(); // the identifier + + TryConsumeToken(tok::ellipsis, EllipsisLoc); + + if (isEndOfTemplateReflectOperand(Tok)) { + bool MemberOfUnknownSpecialization; + TemplateNameKind TNK = Actions.isTemplateName( + getCurScope(), SS, + /*hasTemplateKeyword=*/false, Name, + /*ObjectType=*/nullptr, + /*EnteringContext=*/false, Template, MemberOfUnknownSpecialization); + if (TNK == TNK_Dependent_template_name || TNK == TNK_Type_template || + TNK == TNK_Function_template || TNK == TNK_Var_template || + TNK == TNK_Concept_template) { + // We have an id-expression that refers to a template usable as an + // operand to a reflect expression. + Result = ParsedTemplateArgument(SS, Template, Name.StartLocation); + } + } + } + + // If this is a pack expansion, build it as such. + if (EllipsisLoc.isValid() && !Result.isInvalid()) + Result = Actions.ActOnPackExpansion(Result, EllipsisLoc); + + return Result; +} + +ParsedTemplateArgument Parser::ParseIndeterminateSpliceTemplateArgument() { + CXXScopeSpec SS; + if (ParseOptionalCXXScopeSpecifier( + SS, /*ObjectType=*/nullptr, + /*ObjectHasErrors=*/false, /*EnteringContext=*/false, + /*MayBePseudoDestructor=*/nullptr, + /*IsTypename=*/false, /*LastII=*/nullptr, /*OnlyNamespace=*/true) || + SS.isInvalid() || SS.isNotEmpty() || !Tok.is(tok::annot_splice)) + return {}; + + ExprResult ER = getExprAnnotation(Tok); + assert(!ER.isInvalid()); + CXXIndeterminateSpliceExpr *Splice = + cast(ER.get()); + ConsumeAnnotationToken(); + + return Actions.ActOnTemplateIndeterminateSpliceArgument(Splice); +} + /// ParseTemplateArgument - Parse a C++ template argument (C++ [temp.names]). /// /// template-argument: [C++ 14.2] @@ -1475,6 +1574,7 @@ ParsedTemplateArgument Parser::ParseTemplateArgument() { Actions, Sema::ExpressionEvaluationContext::ConstantEvaluated, /*LambdaContextDecl=*/nullptr, /*ExprContext=*/Sema::ExpressionEvaluationContextRecord::EK_TemplateArgument); + if (isCXXTypeId(TypeIdAsTemplateArgument)) { TypeResult TypeArg = ParseTypeName( /*Range=*/nullptr, DeclaratorContext::TemplateArg); @@ -1492,7 +1592,22 @@ ParsedTemplateArgument Parser::ParseTemplateArgument() { return TemplateTemplateArgument; } - // Revert this tentative parse to parse a non-type template argument. + // Revert this tentative parse. + TPA.Revert(); + } + + // Try to parse an indeterminate splice template argument. + { + TentativeParsingAction TPA(*this); + + ParsedTemplateArgument SpliceTemplateArgument + = ParseIndeterminateSpliceTemplateArgument(); + if (!SpliceTemplateArgument.isInvalid()) { + TPA.Commit(); + return SpliceTemplateArgument; + } + + // Revert this tentative parse; assume a non-type template argument. TPA.Revert(); } diff --git a/clang/lib/Parse/ParseTentative.cpp b/clang/lib/Parse/ParseTentative.cpp index ea17c3e3252ec..f471a4987fdc4 100644 --- a/clang/lib/Parse/ParseTentative.cpp +++ b/clang/lib/Parse/ParseTentative.cpp @@ -1,5 +1,7 @@ //===--- ParseTentative.cpp - Ambiguity Resolution Parsing ----------------===// // +// Copyright 2024 Bloomberg Finance L.P. +// // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception @@ -693,6 +695,9 @@ bool Parser::isCXXTypeId(TentativeCXXTypeIdContext Context, bool &isAmbiguous) { } else if (Context == TypeIdInTrailingReturnType) { TPR = TPResult::True; isAmbiguous = true; + } else if (Context == TypeIdAsReflectionOperand) { + TPR = TPResult::True; + isAmbiguous = true; } else TPR = TPResult::False; } @@ -1120,6 +1125,10 @@ Parser::TPResult Parser::TryParseDeclarator(bool mayBeAbstract, ConsumeToken(); } else if (Tok.is(tok::l_paren)) { ConsumeParen(); + + if (Tok.is(tok::l_splice) && TryAnnotateTypeOrScopeToken()) + return TPResult::Error; + if (mayBeAbstract && (Tok.is(tok::r_paren) || // 'int()' is a function. // 'int(...)' is a function. @@ -1291,6 +1300,7 @@ class TentativeParseCCC final : public CorrectionCandidateCallback { /// [GNU] '__auto_type' /// [C++11] 'decltype' ( expression ) /// [C++1y] 'decltype' ( 'auto' ) +/// [C++2c] '[:' expression ':]' /// /// type-name: /// class-name @@ -1464,6 +1474,7 @@ Parser::isCXXDeclarationSpecifier(ImplicitTypenameContext AllowImplicitTypename, } case tok::kw___super: case tok::kw_decltype: + case tok::l_splice: // Annotate typenames and C++ scope specifiers. If we get one, just // recurse to handle whatever we get. if (TryAnnotateTypeOrScopeToken(AllowImplicitTypename)) diff --git a/clang/lib/Parse/Parser.cpp b/clang/lib/Parse/Parser.cpp index cc0e41ed221c4..3fdae7d38a4ed 100644 --- a/clang/lib/Parse/Parser.cpp +++ b/clang/lib/Parse/Parser.cpp @@ -1,5 +1,7 @@ //===--- Parser.cpp - C Language Family Parser ----------------------------===// // +// Copyright 2024 Bloomberg Finance L.P. +// // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception @@ -368,6 +370,14 @@ bool Parser::SkipUntil(ArrayRef Toks, SkipUntilFlags Flags) { else SkipUntil(tok::r_brace); break; + case tok::l_splice: + // Recursively skip property-nested splices. + ConsumeSplice(); + if (HasFlagsSet(Flags, StopAtCodeCompletion)) + SkipUntil(tok::r_splice, StopAtCodeCompletion); + else + SkipUntil(tok::r_splice); + break; case tok::question: // Recursively skip ? ... : pairs; these function as brackets. But // still stop at a semicolon if requested. @@ -397,6 +407,11 @@ bool Parser::SkipUntil(ArrayRef Toks, SkipUntilFlags Flags) { return false; // Matches something. ConsumeBrace(); break; + case tok::r_splice: + if (SpliceCount && !isFirstTokenSkipped) + return false; // Matches something. + ConsumeSplice(); + break; case tok::semi: if (HasFlagsSet(Flags, StopAtSemi)) @@ -1996,7 +2011,7 @@ bool Parser::TryAnnotateTypeOrScopeToken( Tok.is(tok::kw_typename) || Tok.is(tok::annot_cxxscope) || Tok.is(tok::kw_decltype) || Tok.is(tok::annot_template_id) || Tok.is(tok::kw___super) || Tok.is(tok::kw_auto) || - Tok.is(tok::annot_pack_indexing_type)) && + Tok.is(tok::l_splice) || Tok.is(tok::annot_pack_indexing_type)) && "Cannot be a type or scope token!"); if (Tok.is(tok::kw_typename)) { @@ -2024,13 +2039,15 @@ bool Parser::TryAnnotateTypeOrScopeToken( // 'typename' '::' [opt] nested-name-specifier template [opt] // simple-template-id SourceLocation TypenameLoc = ConsumeToken(); + CXXScopeSpec SS; if (ParseOptionalCXXScopeSpecifier(SS, /*ObjectType=*/nullptr, /*ObjectHasErrors=*/false, /*EnteringContext=*/false, nullptr, /*IsTypename*/ true)) return true; - if (SS.isEmpty()) { + + if (SS.isEmpty() && !Tok.is(tok::annot_splice)) { if (Tok.is(tok::identifier) || Tok.is(tok::annot_template_id) || Tok.is(tok::annot_decltype)) { // Attempt to recover by skipping the invalid 'typename' @@ -2077,6 +2094,15 @@ bool Parser::TryAnnotateTypeOrScopeToken( TemplateId->Template, TemplateId->Name, TemplateId->TemplateNameLoc, TemplateId->LAngleLoc, TemplateArgsPtr, TemplateId->RAngleLoc); + } else if (Tok.is(tok::annot_splice)) { + // We parsed a 'typename' keyword, so this must be a type. + Token SpliceToken = Tok; + Ty = ParseCXXSpliceAsType(/*AllowDependent=*/true, /*Complain=*/true); + if (Ty.isInvalid()) + return true; + + // Unconsume splice token so it can be replaced with 'annot-typename'. + UnconsumeToken(SpliceToken); } else { Diag(Tok, diag::err_expected_type_name_after_typename) << SS.getRange(); diff --git a/clang/lib/Sema/CMakeLists.txt b/clang/lib/Sema/CMakeLists.txt index ab3b813a9ccd9..7d5d12416e1f5 100644 --- a/clang/lib/Sema/CMakeLists.txt +++ b/clang/lib/Sema/CMakeLists.txt @@ -20,6 +20,7 @@ add_clang_library(clangSema HLSLExternalSemaSource.cpp IdentifierResolver.cpp JumpDiagnostics.cpp + Metafunctions.cpp MultiplexExternalSemaSource.cpp ParsedAttr.cpp Scope.cpp @@ -58,6 +59,7 @@ add_clang_library(clangSema SemaOpenMP.cpp SemaOverload.cpp SemaPseudoObject.cpp + SemaReflect.cpp SemaRISCVVectorLookup.cpp SemaStmt.cpp SemaStmtAsm.cpp diff --git a/clang/lib/Sema/DeclSpec.cpp b/clang/lib/Sema/DeclSpec.cpp index b79683bb32a69..46a51760a2b82 100644 --- a/clang/lib/Sema/DeclSpec.cpp +++ b/clang/lib/Sema/DeclSpec.cpp @@ -1,5 +1,7 @@ //===--- DeclSpec.cpp - Declaration Specifier Semantic Analysis -----------===// // +// Copyright 2024 Bloomberg Finance L.P. +// // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception @@ -123,6 +125,18 @@ void CXXScopeSpec::MakeSuper(ASTContext &Context, CXXRecordDecl *RD, "NestedNameSpecifierLoc range computation incorrect"); } +void CXXScopeSpec::MakeIndeterminateSplice(ASTContext &Context, + CXXIndeterminateSpliceExpr * Expr, + SourceLocation ColonColonLoc) { + Builder.MakeIndeterminateSplice(Context, Expr, ColonColonLoc); + + Range.setBegin(Expr->getLSpliceLoc()); + Range.setEnd(ColonColonLoc); + + assert(Range == Builder.getSourceRange() && + "NestedNameSpecifierLoc range computation incorrect"); +} + void CXXScopeSpec::MakeTrivial(ASTContext &Context, NestedNameSpecifier *Qualifier, SourceRange R) { Builder.MakeTrivial(Context, Qualifier, R); @@ -385,6 +399,7 @@ bool Declarator::isDeclarationOfFunction() const { return false; case TST_decltype: + case TST_type_splice: case TST_typeof_unqualExpr: case TST_typeofExpr: if (Expr *E = DS.getRepAsExpr()) @@ -595,6 +610,7 @@ const char *DeclSpec::getSpecifierName(DeclSpec::TST T, case DeclSpec::TST_auto: return "auto"; case DeclSpec::TST_auto_type: return "__auto_type"; case DeclSpec::TST_decltype: return "(decltype)"; + case DeclSpec::TST_type_splice: return "[:reflection-of-type:]"; case DeclSpec::TST_decltype_auto: return "decltype(auto)"; #define TRANSFORM_TYPE_TRAIT_DEF(_, Trait) \ case DeclSpec::TST_##Trait: \ diff --git a/clang/lib/Sema/Metafunctions.cpp b/clang/lib/Sema/Metafunctions.cpp new file mode 100644 index 0000000000000..3fa124418d1b3 --- /dev/null +++ b/clang/lib/Sema/Metafunctions.cpp @@ -0,0 +1,3707 @@ +//===--- Metafunctions.cpp - Functions targeting reflections ----*- C++ -*-===// +// +// Copyright 2024 Bloomberg Finance L.P. +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This file implements all metafunctions from the header. +// +//===----------------------------------------------------------------------===// + +#include "clang/AST/APValue.h" +#include "clang/AST/CXXInheritance.h" +#include "clang/AST/DeclCXX.h" +#include "clang/AST/DeclTemplate.h" +#include "clang/AST/Expr.h" +#include "clang/AST/RecordLayout.h" +#include "clang/AST/Reflection.h" +#include "clang/Basic/SourceManager.h" +#include "clang/Lex/Preprocessor.h" +#include "clang/Sema/Metafunction.h" +#include "clang/Sema/ParsedTemplate.h" +#include "clang/Sema/Sema.h" +#include "clang/Sema/Template.h" +#include "clang/Sema/TemplateDeduction.h" +#include "llvm/ADT/StringExtras.h" + + +namespace clang { + +using EvalFn = Metafunction::EvaluateFn; + +// ----------------------------------------------------------------------------- +// P2996 Metafunction declarations +// ----------------------------------------------------------------------------- + +static bool get_begin_enumerator_decl_of(APValue &Result, Sema &S, + EvalFn Evaluator, QualType ResultTy, SourceRange Range, + ArrayRef Args); + +static bool get_next_enumerator_decl_of(APValue &Result, Sema &S, + EvalFn Evaluator, QualType ResultTy, + SourceRange Range, ArrayRef Args); + +static bool get_ith_base_of(APValue &Result, Sema &S, + EvalFn Evaluator, QualType ResultTy, + SourceRange Range, ArrayRef Args); + +static bool get_ith_template_argument_of(APValue &Result, Sema &S, + EvalFn Evaluator, QualType ResultTy, + SourceRange Range, + ArrayRef Args); + +static bool get_begin_member_decl_of(APValue &Result, Sema &S, EvalFn Evaluator, + QualType ResultTy, SourceRange Range, + ArrayRef Args); + +static bool get_next_member_decl_of(APValue &Result, Sema &S, EvalFn Evaluator, + QualType ResultTy, SourceRange Range, + ArrayRef Args); + +static bool map_decl_to_entity(APValue &Result, Sema &S, EvalFn Evaluator, + QualType ResultTy, SourceRange Range, + ArrayRef Args); + +static bool name_of(APValue &Result, Sema &S, EvalFn Evaluator, + QualType ResultTy, SourceRange Range, + ArrayRef Args); + +static bool display_name_of(APValue &Result, Sema &S, EvalFn Evaluator, + QualType ResultTy, SourceRange Range, + ArrayRef Args); + +static bool source_location_of(APValue &Result, Sema &S, EvalFn Evaluator, + QualType ResultTy, SourceRange Range, + ArrayRef Args); + +static bool type_of(APValue &Result, Sema &S, EvalFn Evaluator, + QualType ResultTy, SourceRange Range, + ArrayRef Args); + +static bool parent_of(APValue &Result, Sema &S, EvalFn Evaluator, + QualType ResultTy, SourceRange Range, + ArrayRef Args); + +static bool dealias(APValue &Result, Sema &S, EvalFn Evaluator, + QualType ResultTy, SourceRange Range, + ArrayRef Args); + +static bool template_of(APValue &Result, Sema &S, EvalFn Evaluator, + QualType ResultTy, SourceRange Range, + ArrayRef Args); + +static bool can_substitute(APValue &Result, Sema &S, EvalFn Evaluator, + QualType ResultTy, SourceRange Range, + ArrayRef Args); + +static bool substitute(APValue &Result, Sema &S, EvalFn Evaluator, + QualType ResultTy, SourceRange Range, + ArrayRef Args); + +static bool value_of(APValue &Result, Sema &S, EvalFn Evaluator, + QualType ResultTy, SourceRange Range, + ArrayRef Args); + +static bool is_public(APValue &Result, Sema &S, EvalFn Evaluator, + QualType ResultTy, SourceRange Range, + ArrayRef Args); + +static bool is_protected(APValue &Result, Sema &S, EvalFn Evaluator, + QualType ResultTy, SourceRange Range, + ArrayRef Args); + +static bool is_private(APValue &Result, Sema &S, EvalFn Evaluator, + QualType ResultTy, SourceRange Range, + ArrayRef Args); + +static bool is_accessible(APValue &Result, Sema &S, EvalFn Evaluator, + QualType ResultTy, SourceRange Range, + ArrayRef Args); + +static bool is_virtual(APValue &Result, Sema &S, EvalFn Evaluator, + QualType ResultTy, SourceRange Range, + ArrayRef Args); + +static bool is_pure_virtual(APValue &Result, Sema &S, EvalFn Evaluator, + QualType ResultTy, SourceRange Range, + ArrayRef Args); + +static bool is_override(APValue &Result, Sema &S, EvalFn Evaluator, + QualType ResultTy, SourceRange Range, + ArrayRef Args); + +static bool is_deleted(APValue &Result, Sema &S, EvalFn Evaluator, + QualType ResultTy, SourceRange Range, + ArrayRef Args); + +static bool is_defaulted(APValue &Result, Sema &S, EvalFn Evaluator, + QualType ResultTy, SourceRange Range, + ArrayRef Args); + +static bool is_explicit(APValue &Result, Sema &S, EvalFn Evaluator, + QualType ResultTy, SourceRange Range, + ArrayRef Args); + +static bool is_bit_field(APValue &Result, Sema &S, EvalFn Evaluator, + QualType ResultTy, SourceRange Range, + ArrayRef Args); + +static bool has_static_storage_duration(APValue &Result, Sema &S, + EvalFn Evaluator, QualType ResultTy, + SourceRange Range, + ArrayRef Args); + +static bool has_internal_linkage(APValue &Result, Sema &S, EvalFn Evaluator, + QualType ResultTy, SourceRange Range, + ArrayRef Args); + +static bool has_external_linkage(APValue &Result, Sema &S, EvalFn Evaluator, + QualType ResultTy, SourceRange Range, + ArrayRef Args); + +static bool has_linkage(APValue &Result, Sema &S, EvalFn Evaluator, + QualType ResultTy, SourceRange Range, + ArrayRef Args); + +static bool is_class_member(APValue &Result, Sema &S, EvalFn Evaluator, + QualType ResultTy, SourceRange Range, + ArrayRef Args); + +static bool is_namespace_member(APValue &Result, Sema &S, EvalFn Evaluator, + QualType ResultTy, SourceRange Range, + ArrayRef Args); + +static bool is_nonstatic_data_member(APValue &Result, Sema &S, + EvalFn Evaluator, QualType ResultTy, + SourceRange Range, + ArrayRef Args); + +static bool is_static_member(APValue &Result, Sema &S, EvalFn Evaluator, + QualType ResultTy, SourceRange Range, + ArrayRef Args); + +static bool is_base(APValue &Result, Sema &S, EvalFn Evaluator, + QualType ResultTy, SourceRange Range, + ArrayRef Args); + +static bool is_namespace(APValue &Result, Sema &S, EvalFn Evaluator, + QualType ResultTy, SourceRange Range, + ArrayRef Args); + +static bool is_function(APValue &Result, Sema &S, EvalFn Evaluator, + QualType ResultTy, SourceRange Range, + ArrayRef Args); + +static bool is_variable(APValue &Result, Sema &S, EvalFn Evaluator, + QualType ResultTy, SourceRange Range, + ArrayRef Args); + +static bool is_type(APValue &Result, Sema &S, EvalFn Evaluator, + QualType ResultTy, SourceRange Range, + ArrayRef Args); + +static bool is_alias(APValue &Result, Sema &S, EvalFn Evaluator, + QualType ResultTy, SourceRange Range, + ArrayRef Args); + +static bool is_incomplete_type(APValue &Result, Sema &S, EvalFn Evaluator, + QualType ResultTy, SourceRange Range, + ArrayRef Args); + +static bool is_template(APValue &Result, Sema &S, EvalFn Evaluator, + QualType ResultTy, SourceRange Range, + ArrayRef Args); + +static bool is_function_template(APValue &Result, Sema &S, EvalFn Evaluator, + QualType ResultTy, SourceRange Range, + ArrayRef Args); + +static bool is_variable_template(APValue &Result, Sema &S, EvalFn Evaluator, + QualType ResultTy, SourceRange Range, + ArrayRef Args); + +static bool is_class_template(APValue &Result, Sema &S, EvalFn Evaluator, + QualType ResultTy, SourceRange Range, + ArrayRef Args); + +static bool is_alias_template(APValue &Result, Sema &S, EvalFn Evaluator, + QualType ResultTy, SourceRange Range, + ArrayRef Args); + +static bool is_concept(APValue &Result, Sema &S, EvalFn Evaluator, + QualType ResultTy, SourceRange Range, + ArrayRef Args); + +static bool has_template_arguments(APValue &Result, Sema &S, EvalFn Evaluator, + QualType ResultTy, SourceRange Range, + ArrayRef Args); + +static bool is_constructor(APValue &Result, Sema &S, EvalFn Evaluator, + QualType ResultTy, SourceRange Range, + ArrayRef Args); + +static bool is_destructor(APValue &Result, Sema &S, EvalFn Evaluator, + QualType ResultTy, SourceRange Range, + ArrayRef Args); + +static bool is_special_member(APValue &Result, Sema &S, EvalFn Evaluator, + QualType ResultTy, SourceRange Range, + ArrayRef Args); + +static bool reflect_value(APValue &Result, Sema &S, EvalFn Evaluator, + QualType ResultTy, SourceRange Range, + ArrayRef Args); + +static bool reflect_invoke(APValue &Result, Sema &S, EvalFn Evaluator, + QualType ResultTy, SourceRange Range, + ArrayRef Args); + +static bool data_member_spec(APValue &Result, Sema &S, EvalFn Evaluator, + QualType ResultTy, SourceRange Range, + ArrayRef Args); + +static bool define_class(APValue &Result, Sema &S, EvalFn Evaluator, + QualType ResultTy, SourceRange Range, + ArrayRef Args); + +static bool offset_of(APValue &Result, Sema &S, EvalFn Evaluator, + QualType ResultTy, SourceRange Range, + ArrayRef Args); + +static bool size_of(APValue &Result, Sema &S, EvalFn Evaluator, + QualType ResultTy, SourceRange Range, + ArrayRef Args); + +static bool bit_offset_of(APValue &Result, Sema &S, EvalFn Evaluator, + QualType ResultTy, SourceRange Range, + ArrayRef Args); + +static bool bit_size_of(APValue &Result, Sema &S, EvalFn Evaluator, + QualType ResultTy, SourceRange Range, + ArrayRef Args); + +static bool alignment_of(APValue &Result, Sema &S, EvalFn Evaluator, + QualType ResultTy, SourceRange Range, + ArrayRef Args); + +// ----------------------------------------------------------------------------- +// P3096 Metafunction declarations +// ----------------------------------------------------------------------------- + +static bool get_ith_parameter_of(APValue &Result, Sema &S, EvalFn Evaluator, + QualType ResultTy, SourceRange Range, + ArrayRef Args); + +static bool has_unique_name(APValue &Result, Sema &S, EvalFn Evaluator, + QualType ResultTy, SourceRange Range, + ArrayRef Args); + +static bool has_default_argument(APValue &Result, Sema &S, EvalFn Evaluator, + QualType ResultTy, SourceRange Range, + ArrayRef Args); + +// ----------------------------------------------------------------------------- +// Metafunction table +// +// Order of entries MUST be kept in sync with order of declarations in the +// +// header file. +// ----------------------------------------------------------------------------- + +static constexpr Metafunction Metafunctions[] = { + // Kind, MinArgs, MaxArgs, Impl + + // non-exposed metafunctions + { Metafunction::MFRK_metaInfo, 2, 2, get_begin_enumerator_decl_of }, + { Metafunction::MFRK_metaInfo, 2, 2, get_next_enumerator_decl_of }, + { Metafunction::MFRK_metaInfo, 3, 3, get_ith_base_of }, + { Metafunction::MFRK_metaInfo, 3, 3, get_ith_template_argument_of }, + { Metafunction::MFRK_metaInfo, 2, 2, get_begin_member_decl_of }, + { Metafunction::MFRK_metaInfo, 2, 2, get_next_member_decl_of }, + { Metafunction::MFRK_metaInfo, 1, 1, map_decl_to_entity }, + + // exposed metafunctions + { Metafunction::MFRK_cstring, 1, 1, name_of }, + { Metafunction::MFRK_cstring, 1, 1, display_name_of }, + { Metafunction::MFRK_sourceLoc, 1, 1, source_location_of }, + { Metafunction::MFRK_metaInfo, 1, 1, type_of }, + { Metafunction::MFRK_metaInfo, 1, 1, parent_of }, + { Metafunction::MFRK_metaInfo, 1, 1, dealias }, + { Metafunction::MFRK_metaInfo, 1, 1, template_of }, + { Metafunction::MFRK_bool, 3, 3, can_substitute }, + { Metafunction::MFRK_metaInfo, 3, 3, substitute }, + { Metafunction::MFRK_spliceFromArg, 2, 2, value_of }, + { Metafunction::MFRK_bool, 1, 1, is_public }, + { Metafunction::MFRK_bool, 1, 1, is_protected }, + { Metafunction::MFRK_bool, 1, 1, is_private }, + { Metafunction::MFRK_bool, 1, 1, is_accessible }, + { Metafunction::MFRK_bool, 1, 1, is_virtual }, + { Metafunction::MFRK_bool, 1, 1, is_pure_virtual }, + { Metafunction::MFRK_bool, 1, 1, is_override }, + { Metafunction::MFRK_bool, 1, 1, is_deleted }, + { Metafunction::MFRK_bool, 1, 1, is_defaulted }, + { Metafunction::MFRK_bool, 1, 1, is_explicit }, + { Metafunction::MFRK_bool, 1, 1, is_bit_field }, + { Metafunction::MFRK_bool, 1, 1, has_static_storage_duration }, + { Metafunction::MFRK_bool, 1, 1, has_internal_linkage }, + { Metafunction::MFRK_bool, 1, 1, has_external_linkage }, + { Metafunction::MFRK_bool, 1, 1, has_linkage }, + { Metafunction::MFRK_bool, 1, 1, is_class_member }, + { Metafunction::MFRK_bool, 1, 1, is_namespace_member }, + { Metafunction::MFRK_bool, 1, 1, is_nonstatic_data_member }, + { Metafunction::MFRK_bool, 1, 1, is_static_member }, + { Metafunction::MFRK_bool, 1, 1, is_base }, + { Metafunction::MFRK_bool, 1, 1, is_namespace }, + { Metafunction::MFRK_bool, 1, 1, is_function }, + { Metafunction::MFRK_bool, 1, 1, is_variable }, + { Metafunction::MFRK_bool, 1, 1, is_type }, + { Metafunction::MFRK_bool, 1, 1, is_alias }, + { Metafunction::MFRK_bool, 1, 1, is_incomplete_type }, + { Metafunction::MFRK_bool, 1, 1, is_template }, + { Metafunction::MFRK_bool, 1, 1, is_function_template }, + { Metafunction::MFRK_bool, 1, 1, is_variable_template }, + { Metafunction::MFRK_bool, 1, 1, is_class_template }, + { Metafunction::MFRK_bool, 1, 1, is_alias_template }, + { Metafunction::MFRK_bool, 1, 1, is_concept }, + { Metafunction::MFRK_bool, 1, 1, has_template_arguments }, + { Metafunction::MFRK_bool, 1, 1, is_constructor }, + { Metafunction::MFRK_bool, 1, 1, is_destructor }, + { Metafunction::MFRK_bool, 1, 1, is_special_member }, + { Metafunction::MFRK_metaInfo, 1, 1, reflect_value }, + { Metafunction::MFRK_metaInfo, 3, 3, reflect_invoke }, + { Metafunction::MFRK_metaInfo, 9, 9, data_member_spec }, + { Metafunction::MFRK_metaInfo, 3, 3, define_class }, + { Metafunction::MFRK_sizeT, 1, 1, offset_of }, + { Metafunction::MFRK_sizeT, 1, 1, size_of }, + { Metafunction::MFRK_sizeT, 1, 1, bit_offset_of }, + { Metafunction::MFRK_sizeT, 1, 1, bit_size_of }, + { Metafunction::MFRK_sizeT, 1, 1, alignment_of }, + + // P3096 metafunction extensions + { Metafunction::MFRK_metaInfo, 3, 3, get_ith_parameter_of }, + { Metafunction::MFRK_bool, 1, 1, has_unique_name }, + { Metafunction::MFRK_bool, 1, 1, has_default_argument }, +}; +constexpr const unsigned NumMetafunctions = sizeof(Metafunctions) / + sizeof(Metafunction); + + +// ----------------------------------------------------------------------------- +// class Metafunction implementation +// ----------------------------------------------------------------------------- + +bool Metafunction::evaluate(APValue &Result, Sema &S, EvalFn Evaluator, + QualType ResultTy, SourceRange Range, + ArrayRef Args) const { + return ImplFn(Result, S, Evaluator, ResultTy, Range, Args); +} + +bool Metafunction::Lookup(unsigned ID, const Metafunction *&result) { + if (ID >= NumMetafunctions) + return true; + + result = &Metafunctions[ID]; + return false; +} + + +// ----------------------------------------------------------------------------- +// Metafunction helper functions +// ----------------------------------------------------------------------------- + +static APValue makeBool(ASTContext &C, bool B) { + return APValue(C.MakeIntValue(B, C.BoolTy)); +} + +static APValue makeReflection(QualType QT) { + return APValue(ReflectionValue::RK_type, QT.getAsOpaquePtr()); +} + +static APValue makeReflection(Decl *D) { + if (isa(D) || isa(D) || + isa(D)) + return APValue(ReflectionValue::RK_namespace, D); + + return APValue(ReflectionValue::RK_declaration, D); +} + +static APValue makeReflection(TemplateName TName) { + return APValue(ReflectionValue::RK_template, TName.getAsVoidPointer()); +} + +static APValue makeReflection(CXXBaseSpecifier *Base) { + return APValue(ReflectionValue::RK_base_specifier, Base); +} + +static APValue makeReflection(TagDataMemberSpec *TDMS) { + return APValue(ReflectionValue::RK_data_member_spec, TDMS); +} + +static bool makeCString(APValue &Result, StringRef Str, ASTContext &C, + EvalFn Evaluator, SourceLocation Loc = {}) { + // Get the type for 'const char[Str.size()]'. + QualType StrLitTy = + C.getConstantArrayType(C.CharTy.withConst(), + llvm::APInt(32, Str.size() + 1), + nullptr, ArraySizeModifier::Normal, 0); + + // Create a string literal having type 'const char [Str.size()]'. + StringLiteral *StrLit = StringLiteral::Create(C, Str, + StringLiteralKind::Ordinary, + false, StrLitTy, Loc); + + // Create an expression to implicitly cast the literal to 'const char *'. + QualType ConstCharPtrTy = C.getPointerType(C.getConstType(C.CharTy)); + Expr *StrExpr = ImplicitCastExpr::Create(C, ConstCharPtrTy, + CK_ArrayToPointerDecay, StrLit, + /*BasePath=*/nullptr, VK_PRValue, + FPOptionsOverride()); + + // Return the evaluated constant expression. + return !Evaluator(Result, StrExpr, true); +} + +static bool SetAndSucceed(APValue &Out, const APValue &Result) { + Out = Result; + return false; +} + +static APValue getTypeName(ASTContext &C, EvalFn Evaluator, QualType QT, + bool emptyIfUnnamed) { + // Determine if this is an unnamed entity. + bool renderEmpty = false; + if (emptyIfUnnamed) { + if (const TagType *TT = dyn_cast(QT)) + renderEmpty = (TT->getDecl()->getName().size() == 0); + } + + // Set the policy for printing the type. + PrintingPolicy PP = C.getPrintingPolicy(); + PP.SuppressTagKeyword = true; + + // Return the type name, defaulting to the empty string if it has no name. + APValue Result; + if (renderEmpty) { + if (makeCString(Result, "", C, Evaluator)) + llvm_unreachable("could not create empty string"); + } else if (makeCString(Result, QT.getAsString(PP), C, Evaluator)) + llvm_unreachable("failed to get type name"); + + return Result; +} + +static APValue getDeclName(ASTContext &C, EvalFn Evaluator, const Decl *D, + bool emptyIfUnnamed) { + std::string Name; + if (const auto *ND = dyn_cast(D)) + Name = ND->getNameAsString(); + + // Return the declaration name. + APValue Result; + if (makeCString(Result, Name, C, Evaluator)) + llvm_unreachable("could not create string for declaration name"); + + return Result; +} + +static APValue getTemplateName(ASTContext &C, EvalFn Evaluator, + TemplateName TName, bool emptyIfUnnamed) { + std::string Name; + { + llvm::raw_string_ostream NameOut(Name); + TName.print(NameOut, C.getPrintingPolicy()); + } + + // Return the template name. + APValue Result; + if (makeCString(Result, Name, C, Evaluator)) + llvm_unreachable("could not create string for template name"); + + return Result; +} + +static NamedDecl *findTypeDecl(QualType QT) { + // If it's an ElaboratedType, get the underlying NamedType. + if (const ElaboratedType *ET = dyn_cast(QT)) + QT = ET->getNamedType(); + + // Get the type's declaration. + NamedDecl *D = nullptr; + if (auto *TD = QT->getAsTagDecl()) + return TD; + else if (auto *TT = dyn_cast(QT)) + D = TT->getDecl(); + else if (auto *TDT = dyn_cast(QT)) + D = TDT->getDecl(); + else if (auto *UT = dyn_cast(QT)) + D = UT->getFoundDecl(); + else if (auto *UUTD = dyn_cast(QT)) + D = UUTD->getDecl(); + else if (auto *TS = dyn_cast(QT)) { + if (auto *CTD = dyn_cast( + TS->getTemplateName().getAsTemplateDecl())) { + void *InsertPos; + D = CTD->findSpecialization(TS->template_arguments(), InsertPos); + } + } else if (auto *STTP = dyn_cast(QT)) + D = findTypeDecl(STTP->getReplacementType()); + else if (auto *ICNT = dyn_cast(QT)) + D = ICNT->getDecl(); + else if (auto *DTT = dyn_cast(QT)) + D = findTypeDecl(DTT->getUnderlyingType()); + + return D; +} + +static bool findTypeDeclLoc(APValue &Result, ASTContext &C, EvalFn Evaluator, + QualType ResultTy, QualType QT) { + // If it's an ElaboratedType, get the underlying NamedType. + if (const ElaboratedType *ET = dyn_cast(QT)) + QT = ET->getNamedType(); + + // Get the type's declaration. + NamedDecl *D = const_cast(findTypeDecl(QT)); + + SourceLocExpr *SLE = + new (C) SourceLocExpr(C, SourceLocIdentKind::SourceLocStruct, + ResultTy, + D ? D->getLocation() : SourceLocation(), + SourceLocation(), + D ? D->getDeclContext() : nullptr); + + return !Evaluator(Result, SLE, true); +} + +static bool findDeclLoc(APValue &Result, ASTContext &C, EvalFn Evaluator, + QualType ResultTy, Decl *D) { + SourceLocExpr *SLE = + new (C) SourceLocExpr(C, SourceLocIdentKind::SourceLocStruct, + ResultTy, + D ? D->getLocation() : SourceLocation(), + SourceLocation(), + D ? D->getDeclContext() : nullptr); + return !Evaluator(Result, SLE, true); +} + +static bool findBaseSpecLoc(APValue &Result, ASTContext &C, EvalFn Evaluator, + QualType ResultTy, CXXBaseSpecifier *B) { + SourceLocExpr *SLE = + new (C) SourceLocExpr(C, SourceLocIdentKind::SourceLocStruct, + ResultTy, B->getBeginLoc(), + SourceLocation(), nullptr); + return !Evaluator(Result, SLE, true); +} + +static QualType dealiasType(ASTContext &C, QualType QT) { + while (true) { + if (const ElaboratedType *ET = dyn_cast(QT)) + QT = ET->getNamedType(); + else if (auto *TDT = dyn_cast(QT)) + QT = TDT->desugar(); + else if (auto *UT = dyn_cast(QT)) + QT = UT->desugar(); + else if (auto *TST = dyn_cast(QT); + TST && TST->isTypeAlias()) + QT = TST->getAliasedType(); + else + break; + } + return QT; +} + +static TemplateName findTemplateOfType(QualType QT) { + // If it's an ElaboratedType, get the underlying NamedType. + if (const ElaboratedType *ET = dyn_cast(QT)) + QT = ET->getNamedType(); + + if (!isa(QT)) + return TemplateName(); + + return dyn_cast(QT)->getTemplateName(); +} + +static TemplateName findTemplateOfDecl(const Decl *D) { + TemplateDecl *TDecl = nullptr; + if (const auto *FD = dyn_cast(D)) { + if (FunctionTemplateSpecializationInfo *Info = + FD->getTemplateSpecializationInfo()) + TDecl = Info->getTemplate(); + } else if (const auto *VD = dyn_cast(D)) { + if (const auto *P = VD->getTemplateInstantiationPattern()) + VD = P; + TDecl = VD->getDescribedVarTemplate(); + } + return TDecl ? TemplateName(TDecl) : TemplateName(); +} + +static bool isTypeAlias(QualType QT) { + // If it's an ElaboratedType, get the underlying NamedType. + if (const ElaboratedType *ET = dyn_cast(QT)) + QT = ET->getNamedType(); + + // If it's a TypedefType, it's an alias. + return QT->isTypedefNameType(); +} + +static SmallVector expandTemplateArgPacks( + ArrayRef Args) { + SmallVector Result; + for (const TemplateArgument &Arg : Args) + if (Arg.getKind() == TemplateArgument::Pack) + for (const TemplateArgument &TA : Arg.getPackAsArray()) + Result.emplace_back(TA); + else + Result.emplace_back(Arg); + + return Result; +} + +bool getTemplateArgumentsFromType(QualType QT, + ArrayRef &Out) { + // Obtain the template arguments from the Type* representation + if (auto asTmplSpecialization = QT->getAs()) + Out = asTmplSpecialization->template_arguments(); + else if (auto DTST = QT->getAs()) + Out = DTST->template_arguments(); + else if (auto *CTSD = dyn_cast_or_null( + QT->getAsRecordDecl())) + Out = expandTemplateArgPacks(CTSD->getTemplateArgs().asArray()); + else + return true; + + return false; +} + +bool getTemplateArgumentsFromDeclaration(Decl* D, + ArrayRef &Out) { + if (auto FD = dyn_cast(D)) { + if (auto templArgs = FD->getTemplateSpecializationArgs()) { + Out = templArgs->asArray(); + return false; + } + } else if (auto VTSD = dyn_cast(D)) { + Out = VTSD->getTemplateArgs().asArray(); + return false; + } + return true; +} + +static APValue getNthTemplateArgument(Sema &S, + ArrayRef templateArgs, + EvalFn Evaluator, APValue Sentinel, + size_t Idx) { + if (Idx >= templateArgs.size()) { + return Sentinel; + } + + const auto& templArgument = templateArgs[Idx]; + switch (templArgument.getKind()) { + // Works for type parameters pack as well + case TemplateArgument::Type: + return makeReflection(templArgument.getAsType()); + // Works for non-template parameters and parameter packs of types: + // int, pointers + case TemplateArgument::Expression: { + Expr *TExpr = templArgument.getAsExpr(); + + APValue ArgResult; + bool success = Evaluator(ArgResult, TExpr, true); + assert(success); + + if (ArgResult.isReflection()) + return APValue(ArgResult.getReflection().getKind(), + ArgResult.getReflection().getOpaqueValue()); + + ConstantExpr *CE = + ConstantExpr::CreateEmpty(S.Context, + ConstantResultStorageKind::APValue); + CE->setType(TExpr->getType()); + CE->setValueKind(TExpr->getValueKind()); + CE->SetResult(ArgResult, S.Context); + + return APValue(ReflectionValue::RK_const_value, CE); + } + case TemplateArgument::Template: + return makeReflection(templArgument.getAsTemplate()); + case TemplateArgument::Reflection: { + const ReflectionValue& asReflection = templArgument.getAsReflection(); + return APValue(asReflection.getKind(), asReflection.getOpaqueValue()); + } + case TemplateArgument::Declaration: + return makeReflection(templArgument.getAsDecl()); + case TemplateArgument::Pack: + llvm_unreachable("Packs should be expanded before calling this"); + + // Could not get a test case to hit one of the below + case TemplateArgument::Null: + llvm_unreachable("TemplateArgument::Null not supported"); + case TemplateArgument::NullPtr: + llvm_unreachable("TemplateArgument::NullPtr not supported"); + case TemplateArgument::StructuralValue: + llvm_unreachable("StructuralValue not implemented"); + case TemplateArgument::Integral: { + ConstantExpr *CE = + ConstantExpr::CreateEmpty(S.Context, + ConstantResultStorageKind::APValue); + CE->setType(S.Context.IntTy); + CE->setValueKind(VK_PRValue); + CE->SetResult(APValue(templArgument.getAsIntegral()), S.Context); + + return APValue(ReflectionValue::RK_const_value, CE); + } + case TemplateArgument::IndeterminateSplice: + llvm_unreachable("TemplateArgument::IndeterminateSplice should have been " + "transformed by now"); + case TemplateArgument::TemplateExpansion: + llvm_unreachable("TemplateArgument::TemplateExpansion not supported"); + } + llvm_unreachable("Unknown template argument type"); +} + +static bool isTemplateSpecialization(QualType QT) { + if (isa(QT) || isa(QT)) + return false; + + return isa(QT) || + isa(QT); +} + +static size_t getBitOffsetOfField(ASTContext &C, const FieldDecl *FD) { + const RecordDecl *Parent = FD->getParent(); + assert(Parent && "no parent for field!"); + + const ASTRecordLayout &Layout = C.getASTRecordLayout(Parent); + return Layout.getFieldOffset(FD->getFieldIndex()); +} + +static TemplateArgumentListInfo addLocToTemplateArgs( + Sema &S, ArrayRef Args, Expr *InstExpr) { + + TemplateArgumentListInfo Result; + for (const TemplateArgument &Arg : expandTemplateArgPacks(Args)) + Result.addArgument( + S.getTrivialTemplateArgumentLoc(Arg, + Arg.getNonTypeTemplateArgumentType(), + InstExpr->getExprLoc())); + return Result; +} + +static bool ensureInstantiated(Sema &S, Decl *D, SourceRange Range) { + auto validateConstraints = [&](TemplateDecl *TDecl, + ArrayRef TArgs) { + MultiLevelTemplateArgumentList MLTAL(TDecl, TArgs, false); + if (S.EnsureTemplateArgumentListConstraints(TDecl, MLTAL, Range)) + return false; + + return true; + }; + + if (auto *CTSD = dyn_cast(D); + CTSD && !CTSD->isCompleteDefinition()) { + if (!validateConstraints(CTSD->getSpecializedTemplate(), + CTSD->getTemplateArgs().asArray())) + return true; + + if (S.InstantiateClassTemplateSpecialization( + Range.getBegin(), CTSD, TSK_ExplicitInstantiationDefinition, false)) + return false; + + S.InstantiateClassTemplateSpecializationMembers( + Range.getBegin(), CTSD, TSK_ExplicitInstantiationDefinition); + } else if (auto *VTSD = dyn_cast(D); + VTSD && !VTSD->isCompleteDefinition()) { + if (!validateConstraints(VTSD->getSpecializedTemplate(), + VTSD->getTemplateArgs().asArray())) + return true; + + S.InstantiateVariableDefinition(Range.getBegin(), VTSD, true, true); + } else if (auto *FD = dyn_cast(D); + FD && FD->isTemplateInstantiation()) { + if (FD->getTemplateSpecializationArgs()) + if (!validateConstraints(FD->getPrimaryTemplate(), + FD->getTemplateSpecializationArgs()->asArray())) + return true; + + S.InstantiateFunctionDefinition(Range.getBegin(), FD, true, true); + } + return true; +} + +static bool ensureDeclared(Sema &S, QualType QT, SourceLocation SpecLoc) { + // If it's an ElaboratedType, get the underlying NamedType. + if (const ElaboratedType *ET = dyn_cast(QT)) + QT = ET->getNamedType(); + + // Get the type's declaration. + if (auto *TS = dyn_cast(QT)) { + if (auto *CTD = dyn_cast( + TS->getTemplateName().getAsTemplateDecl())) { + void *InsertPos; + if (!CTD->findSpecialization(TS->template_arguments(), InsertPos)) { + ClassTemplateSpecializationDecl *D = + ClassTemplateSpecializationDecl::Create( + S.Context, CTD->getTemplatedDecl()->getTagKind(), + CTD->getDeclContext(), SpecLoc, SpecLoc, CTD, + TS->template_arguments(), nullptr); + if (!D) + return false; + + CTD->AddSpecialization(D, InsertPos); + } + } + } + return true; +} + +static bool isReflectableDecl(ASTContext &C, const Decl *D) { + assert(D && "null declaration"); + if (isa(D) || isa(D) || isa(D)) + return false; + if (isa(D)) + return true; + if (auto *Class = dyn_cast(D)) + if (Class->isInjectedClassName()) + return false; + + return D->getCanonicalDecl() == D; +} + +/// Filter non-reflectable members. +static Decl *findIterableMember(ASTContext &C, Decl *D, bool Inclusive) { + if (!D || (Inclusive && isReflectableDecl(C, D))) + return D; + + do { + DeclContext *DC = D->getDeclContext(); + + // Get the next declaration in the DeclContext. + // + // Explicit specializations of templates are created with the DeclContext of + // the template from which they're instantiated, but they end up in the + // DeclContext within which they're declared. We therefore skip over any + // declarations whose DeclContext is different from the previous Decl; + // otherwise, we may inadvertently break the chain of redeclarations in + // difficult to predit ways. + D = D->getNextDeclInContext(); + while (D && D->getDeclContext() != DC) + D = D->getNextDeclInContext(); + + if (auto *NSDecl = dyn_cast(DC)) { + while (!D && NSDecl) { + NSDecl = NSDecl->getPreviousDecl(); + D = NSDecl ? *NSDecl->decls_begin() : nullptr; + } + } + } while (D && !isReflectableDecl(C, D)); + return D; +} + +bool parentOf(APValue &Result, Decl *D) { + if (!D) + return true; + + auto *DC = D->getDeclContext(); + while (DC && !isa(DC) && !isa(DC) && + !isa(DC)) + DC = DC->getParent(); + + if (!DC) + return true; + else if (auto *RD = dyn_cast(DC)) + return SetAndSucceed(Result, + makeReflection(QualType(RD->getTypeForDecl(), 0))); + + return SetAndSucceed(Result, makeReflection(cast(DC))); +} + +bool isSpecialMember(FunctionDecl *FD) { + bool IsSpecial = false; + if (const auto *MD = dyn_cast(FD)) { + IsSpecial = (isa(MD) || + MD->isCopyAssignmentOperator() || + MD->isMoveAssignmentOperator()); + + if (auto *CtorD = dyn_cast(MD)) + IsSpecial = IsSpecial || (CtorD->isDefaultConstructor() || + CtorD->isCopyConstructor() || + CtorD->isMoveConstructor()); + } + return IsSpecial; +} + +bool isAccessible(Sema &S, DeclContext *AccessDC, NamedDecl *D) { + bool Result = false; + + if (auto *ClsDecl = dyn_cast_or_null(D->getDeclContext())) { + CXXRecordDecl *NamingCls = ClsDecl; + for (DeclContext *DC = AccessDC; DC; DC = DC->getParent()) + if (auto *CXXRD = dyn_cast(DC)) { + if (CXXRD->isDerivedFrom(ClsDecl)) { + NamingCls = CXXRD; + break; + } + } + + DeclContext *PreviousDC = S.CurContext; + { + S.CurContext = AccessDC; + Result = S.IsSimplyAccessible(D, NamingCls, QualType()); + S.CurContext = PreviousDC; + } + } + return Result; +} + +// ----------------------------------------------------------------------------- +// Metafunction implementations +// ----------------------------------------------------------------------------- + +bool get_begin_enumerator_decl_of(APValue &Result, Sema &S, EvalFn Evaluator, + QualType ResultTy, SourceRange Range, + ArrayRef Args) { + assert(Args[0]->getType()->isReflectionType()); + assert(ResultTy == S.Context.MetaInfoTy); + + APValue R; + if (!Evaluator(R, Args[0], true)) + return true; + + APValue Sentinel; + if (!Evaluator(Sentinel, Args[1], true)) + return true; + assert(Sentinel.getReflection().getKind() == ReflectionValue::RK_type); + + switch (R.getReflection().getKind()) { + case ReflectionValue::RK_type: { + Decl *D = findTypeDecl(R.getReflectedType()); + + if (auto enumDecl = dyn_cast_or_null(D)) { + if (auto itr = enumDecl->enumerator_begin(); + itr != enumDecl->enumerator_end()) { + return SetAndSucceed(Result, makeReflection(*itr)); + } + return SetAndSucceed(Result, Sentinel); + } + return true; + } + case ReflectionValue::RK_declaration: + case ReflectionValue::RK_template: + case ReflectionValue::RK_const_value: + case ReflectionValue::RK_namespace: + case ReflectionValue::RK_base_specifier: + case ReflectionValue::RK_data_member_spec: { + return true; + } + } + llvm_unreachable("unknown reflection kind"); +} + +bool get_next_enumerator_decl_of(APValue &Result, Sema &S, EvalFn Evaluator, + QualType ResultTy, SourceRange Range, + ArrayRef Args) { + assert(Args[0]->getType()->isReflectionType()); + assert(ResultTy == S.Context.MetaInfoTy); + + APValue R; + if (!Evaluator(R, Args[0], true)) + return true; + + APValue Sentinel; + if (!Evaluator(Sentinel, Args[1], true)) + return true; + assert(Sentinel.getReflection().getKind() == ReflectionValue::RK_type); + + switch (R.getReflection().getKind()) { + case ReflectionValue::RK_declaration: { + Decl *currEnumConstDecl = R.getReflectedDecl(); + if(auto nextEnumConstDecl = currEnumConstDecl->getNextDeclInContext()) { + return SetAndSucceed(Result, makeReflection(nextEnumConstDecl)); + } + return SetAndSucceed(Result, Sentinel); + } + case ReflectionValue::RK_type: + case ReflectionValue::RK_template: + case ReflectionValue::RK_const_value: + case ReflectionValue::RK_namespace: + case ReflectionValue::RK_base_specifier: + case ReflectionValue::RK_data_member_spec: { + return true; + } + } + llvm_unreachable("unknown reflection kind"); +} + +bool get_ith_base_of(APValue &Result, Sema &S, EvalFn Evaluator, + QualType ResultTy, SourceRange Range, + ArrayRef Args) { + assert(Args[0]->getType()->isReflectionType()); + assert(ResultTy == S.Context.MetaInfoTy); + + APValue R; + if (!Evaluator(R, Args[0], true)) + return true; + + APValue Sentinel; + if (!Evaluator(Sentinel, Args[1], true)) + return true; + assert(Sentinel.getReflection().getKind() == ReflectionValue::RK_type); + + APValue Idx; + if (!Evaluator(Idx, Args[2], true)) + return true; + size_t idx = Idx.getInt().getExtValue(); + + switch (R.getReflection().getKind()) { + case ReflectionValue::RK_type: { + Decl *typeDecl = findTypeDecl(R.getReflectedType()); + + if (auto cxxRecordDecl = dyn_cast_or_null(typeDecl)) { + ensureInstantiated(S, typeDecl, Range); + + auto numBases = cxxRecordDecl->getNumBases(); + if (idx >= numBases) + return SetAndSucceed(Result, Sentinel); + + // the unqualified base class + CXXBaseSpecifier *baseClassItr = cxxRecordDecl->bases_begin() + idx; + return SetAndSucceed(Result, makeReflection(baseClassItr)); + } + return true; + } + case ReflectionValue::RK_declaration: + case ReflectionValue::RK_template: + case ReflectionValue::RK_const_value: + case ReflectionValue::RK_namespace: + case ReflectionValue::RK_base_specifier: + case ReflectionValue::RK_data_member_spec: + return true; + } + llvm_unreachable("unknown reflection kind"); +} + +bool get_ith_template_argument_of(APValue &Result, Sema &S, EvalFn Evaluator, + QualType ResultTy, SourceRange Range, + ArrayRef Args) { + assert(Args[0]->getType()->isReflectionType()); + assert(ResultTy == S.Context.MetaInfoTy); + + APValue R; + if (!Evaluator(R, Args[0], true)) + return true; + + APValue Sentinel; + if (!Evaluator(Sentinel, Args[1], true)) + return true; + assert(Sentinel.getReflection().getKind() == ReflectionValue::RK_type); + + APValue Idx; + if (!Evaluator(Idx, Args[2], true)) + return true; + size_t idx = Idx.getInt().getExtValue(); + + switch (R.getReflection().getKind()) { + case ReflectionValue::RK_type: { + QualType QT = R.getReflectedType(); + ArrayRef TArgs; + if (getTemplateArgumentsFromType(QT, TArgs)) + return true; + TArgs = expandTemplateArgPacks(TArgs); + return SetAndSucceed(Result, getNthTemplateArgument(S, TArgs, Evaluator, + Sentinel, idx)); + } + case ReflectionValue::RK_declaration: { + ArrayRef TArgs; + if (getTemplateArgumentsFromDeclaration(R.getReflectedDecl(), TArgs)) + return true; + TArgs = expandTemplateArgPacks(TArgs); + return SetAndSucceed(Result, getNthTemplateArgument(S, TArgs, Evaluator, + Sentinel, idx)); + } + case ReflectionValue::RK_template: + case ReflectionValue::RK_const_value: + case ReflectionValue::RK_namespace: + case ReflectionValue::RK_base_specifier: + case ReflectionValue::RK_data_member_spec: + return true; + } + llvm_unreachable("unknown reflection kind"); +} + +bool get_begin_member_decl_of(APValue &Result, Sema &S, EvalFn Evaluator, + QualType ResultTy, SourceRange Range, + ArrayRef Args) { + assert(ResultTy == S.Context.MetaInfoTy); + + assert(Args[0]->getType()->isReflectionType()); + APValue R; + if (!Evaluator(R, Args[0], true)) { + return true; + } + + assert(Args[1]->getType()->isReflectionType()); + APValue Sentinel; + if (!Evaluator(Sentinel, Args[1], true)) + return true; + assert(Sentinel.getReflection().getKind() == ReflectionValue::RK_type); + + switch (R.getReflection().getKind()) { + case ReflectionValue::RK_type: + { + QualType QT = R.getReflectedType(); + if (isTypeAlias(QT)) + QT = dealiasType(S.Context, QT); + + if (isa(QT)) // should use 'enumerators_of' instead. + return true; + + ensureDeclared(S, QT, Range.getBegin()); + Decl *typeDecl = findTypeDecl(QT); + if (!typeDecl) + return true; + if (auto *CXXRD = dyn_cast(typeDecl)) + S.ForceDeclarationOfImplicitMembers(CXXRD); + + DeclContext *declContext = dyn_cast(typeDecl); + assert(declContext && "no DeclContext?"); + + if (!ensureInstantiated(S, typeDecl, Range)) + llvm_unreachable("could not instantiate"); + + Decl* beginMember = findIterableMember(S.Context, + *declContext->decls_begin(), true); + if (!beginMember) + return SetAndSucceed(Result, Sentinel); + return SetAndSucceed(Result, APValue(ReflectionValue::RK_declaration, + beginMember)); + } + case ReflectionValue::RK_namespace: { + Decl *NS = R.getReflectedNamespace(); + if (auto *A = dyn_cast(NS)) + NS = A->getNamespace(); + + DeclContext *DC = cast(NS->getMostRecentDecl()); + + Decl *beginMember = findIterableMember(S.Context, *DC->decls_begin(), true); + if (!beginMember) + return SetAndSucceed(Result, Sentinel); + return SetAndSucceed(Result, APValue(ReflectionValue::RK_declaration, + beginMember)); + } + case ReflectionValue::RK_declaration: + case ReflectionValue::RK_template: + case ReflectionValue::RK_const_value: + case ReflectionValue::RK_base_specifier: + case ReflectionValue::RK_data_member_spec: + return true; + } + llvm_unreachable("unknown reflection kind"); +} + +bool get_next_member_decl_of(APValue &Result, Sema &S, EvalFn Evaluator, + QualType ResultTy, SourceRange Range, + ArrayRef Args) { + assert(ResultTy == S.Context.MetaInfoTy); + + assert(Args[0]->getType()->isReflectionType()); + APValue R; + if (!Evaluator(R, Args[0], true)) + return true; + + assert(Args[1]->getType()->isReflectionType()); + APValue Sentinel; + if (!Evaluator(Sentinel, Args[1], true)) + return true; + assert(Sentinel.getReflection().getKind() == ReflectionValue::RK_type); + + if (Decl *Next = findIterableMember(S.Context, R.getReflectedDecl(), false)) { + return SetAndSucceed(Result, APValue(ReflectionValue::RK_declaration, + Next)); + } + return SetAndSucceed(Result, Sentinel); +} + +bool map_decl_to_entity(APValue &Result, Sema &S, EvalFn Evaluator, + QualType ResultTy, SourceRange Range, + ArrayRef Args) { + assert(ResultTy == S.Context.MetaInfoTy); + assert(Args[0]->getType()->isReflectionType()); + + APValue R; + if (!Evaluator(R, Args[0], true)) + return true; + Decl *D = R.getReflectedDecl(); + + if (auto *TyDecl = dyn_cast(D)) { + QualType QT = S.Context.getTypeDeclType(TyDecl); + if (auto *TDND = dyn_cast(TyDecl)) { + auto TSI = TDND->getTypeSourceInfo(); + assert(TSI); + QT = TSI->getType(); + } + return SetAndSucceed(Result, makeReflection(QT)); + } else if (auto *TDecl = dyn_cast(D)) { + TemplateName TName(TDecl); + return SetAndSucceed(Result, makeReflection(TName)); + } else { + return SetAndSucceed(Result, makeReflection(D)); + } + llvm_unreachable("unknown reflection kind"); +} + +bool name_of(APValue &Result, Sema &S, EvalFn Evaluator, QualType ResultTy, + SourceRange Range, ArrayRef Args) { + assert(Args[0]->getType()->isReflectionType()); + assert(ResultTy == + S.Context.getPointerType(S.Context.getConstType(S.Context.CharTy))); + + APValue R; + if (!Evaluator(R, Args[0], true)) + return true; + + switch (R.getReflection().getKind()) { + case ReflectionValue::RK_type: { + QualType QT = R.getReflectedType(); + return SetAndSucceed(Result, getTypeName(S.Context, Evaluator, QT, + /*emptyIfUnnamed=*/true)); + } + case ReflectionValue::RK_declaration: { + ValueDecl *D = R.getReflectedDecl(); + return SetAndSucceed(Result, getDeclName(S.Context, Evaluator, D, + /*emptyIfUnnamed=*/true)); + } + case ReflectionValue::RK_template: { + TemplateName TName = R.getReflectedTemplate(); + return SetAndSucceed(Result, getTemplateName(S.Context, Evaluator, TName, + /*emptyIfUnnamed=*/true)); + } + case ReflectionValue::RK_const_value: { + if (makeCString(Result, "", S.Context, Evaluator)) + llvm_unreachable("failed to create empty string"); + return false; + } + case ReflectionValue::RK_namespace: { + Decl *D = R.getReflectedNamespace(); + return SetAndSucceed(Result, getDeclName(S.Context, Evaluator, D, + /*emptyIfUnnamed=*/true)); + } + case ReflectionValue::RK_base_specifier: { + QualType QT = R.getReflectedBaseSpecifier()->getType(); + return SetAndSucceed(Result, getTypeName(S.Context, Evaluator, QT, + /*emptyIfUnnamed=*/true)); + } + case ReflectionValue::RK_data_member_spec: + return true; + } + llvm_unreachable("unknown reflection kind"); +} + +bool display_name_of(APValue &Result, Sema &S, EvalFn Evaluator, + QualType ResultTy, SourceRange Range, + ArrayRef Args) { + assert(Args[0]->getType()->isReflectionType()); + assert(ResultTy == + S.Context.getPointerType(S.Context.getConstType(S.Context.CharTy))); + + APValue R; + if (!Evaluator(R, Args[0], true)) + return true; + + switch (R.getReflection().getKind()) { + case ReflectionValue::RK_type: { + QualType QT = R.getReflectedType(); + return SetAndSucceed(Result, getTypeName(S.Context, Evaluator, QT, + /*emptyIfUnnamed=*/false)); + } + case ReflectionValue::RK_declaration: { + const Decl *D = R.getReflectedDecl(); + return SetAndSucceed(Result, getDeclName(S.Context, Evaluator, D, + /*emptyIfUnnamed=*/false)); + } + case ReflectionValue::RK_template: { + TemplateName TName = R.getReflectedTemplate(); + return SetAndSucceed(Result, getTemplateName(S.Context, Evaluator, TName, + /*emptyIfUnnamed=*/false)); + } + case ReflectionValue::RK_const_value: { + if (makeCString(Result, "", S.Context, Evaluator)) + llvm_unreachable("failed to create empty string"); + return false; + } + case ReflectionValue::RK_namespace: { + Decl *D = R.getReflectedNamespace(); + return SetAndSucceed(Result, getDeclName(S.Context, Evaluator, D, + /*emptyIfUnnamed=*/false)); + } + case ReflectionValue::RK_base_specifier: { + QualType QT = R.getReflectedBaseSpecifier()->getType(); + return SetAndSucceed(Result, getTypeName(S.Context, Evaluator, QT, + /*emptyIfUnnamed=*/false)); + } + case ReflectionValue::RK_data_member_spec: + return true; + } + llvm_unreachable("unknown reflection kind"); +} + +bool source_location_of(APValue &Result, Sema &S, EvalFn Evaluator, + QualType ResultTy, SourceRange Range, + ArrayRef Args) { + assert(Args[0]->getType()->isReflectionType()); + + APValue R; + if (!Evaluator(R, Args[0], true)) + return true; + + switch (R.getReflection().getKind()) { + case ReflectionValue::RK_type: + return findTypeDeclLoc(Result, S.Context, Evaluator, ResultTy, + R.getReflectedType()); + case ReflectionValue::RK_declaration: + return findDeclLoc(Result, S.Context, Evaluator, ResultTy, + R.getReflectedDecl()); + case ReflectionValue::RK_template: { + TemplateName TName = R.getReflectedTemplate(); + return findDeclLoc(Result, S.Context, Evaluator, ResultTy, + TName.getAsTemplateDecl()); + } + case ReflectionValue::RK_namespace: + return findDeclLoc(Result, S.Context, Evaluator, ResultTy, + R.getReflectedNamespace()); + case ReflectionValue::RK_const_value: + return findDeclLoc(Result, S.Context, Evaluator, ResultTy, nullptr); + case ReflectionValue::RK_base_specifier: + return findBaseSpecLoc(Result, S.Context, Evaluator, ResultTy, nullptr); + case ReflectionValue::RK_data_member_spec: + return true; + } + llvm_unreachable("unknown reflection kind"); +} + +bool type_of(APValue &Result, Sema &S, EvalFn Evaluator, QualType ResultTy, + SourceRange Range, ArrayRef Args) { + assert(Args[0]->getType()->isReflectionType()); + + APValue R; + if (!Evaluator(R, Args[0], true)) + return true; + + switch (R.getReflection().getKind()) { + case ReflectionValue::RK_type: + case ReflectionValue::RK_template: + case ReflectionValue::RK_namespace: + case ReflectionValue::RK_data_member_spec: + return true; + case ReflectionValue::RK_const_value: { + ConstantExpr *E = R.getReflectedConstValueExpr(); + return SetAndSucceed(Result, makeReflection(E->getType())); + } + case ReflectionValue::RK_declaration: { + ValueDecl *VD = cast(R.getReflectedDecl()); + return SetAndSucceed(Result, makeReflection(VD->getType())); + } + case ReflectionValue::RK_base_specifier: { + QualType QT = R.getReflectedBaseSpecifier()->getType(); + return SetAndSucceed(Result, makeReflection(QT)); + } + } + llvm_unreachable("unknown reflection kind"); +} + +bool parent_of(APValue &Result, Sema &S, EvalFn Evaluator, QualType ResultTy, + SourceRange Range, ArrayRef Args) { + assert(Args[0]->getType()->isReflectionType()); + + APValue R; + if (!Evaluator(R, Args[0], true)) + return true; + + switch (R.getReflection().getKind()) { + case ReflectionValue::RK_const_value: + case ReflectionValue::RK_data_member_spec: + case ReflectionValue::RK_base_specifier: + return true; + case ReflectionValue::RK_type: { + if (TemplateName TName = findTemplateOfType(R.getReflectedType()); + !TName.isNull()) + return parentOf(Result, TName.getAsTemplateDecl()); + + return parentOf(Result, findTypeDecl(R.getReflectedType())); + } + case ReflectionValue::RK_declaration: { + if (TemplateName TName = findTemplateOfDecl(R.getReflectedDecl()); + !TName.isNull()) + return parentOf(Result, TName.getAsTemplateDecl()); + + return parentOf(Result, R.getReflectedDecl()); + } + case ReflectionValue::RK_template: { + return parentOf(Result, R.getReflectedTemplate().getAsTemplateDecl()); + } + case ReflectionValue::RK_namespace: + return parentOf(Result, R.getReflectedNamespace()); + } + llvm_unreachable("unknown reflection kind"); +} + +bool dealias(APValue &Result, Sema &S, EvalFn Evaluator, QualType ResultTy, + SourceRange Range, ArrayRef Args) { + assert(Args[0]->getType()->isReflectionType()); + assert(ResultTy == S.Context.MetaInfoTy); + + APValue R; + if (!Evaluator(R, Args[0], true)) + return true; + + switch (R.getReflection().getKind()) { + case ReflectionValue::RK_const_value: + case ReflectionValue::RK_declaration: + case ReflectionValue::RK_template: + case ReflectionValue::RK_base_specifier: + case ReflectionValue::RK_data_member_spec: + return true; + case ReflectionValue::RK_type: { + QualType QT = R.getReflectedType(); + QT = dealiasType(S.Context, QT); + return SetAndSucceed(Result, makeReflection(QT)); + } + case ReflectionValue::RK_namespace: { + Decl *NS = R.getReflectedNamespace(); + if (auto *A = dyn_cast(NS)) + NS = A->getNamespace(); + return SetAndSucceed(Result, makeReflection(NS)); + } + } + llvm_unreachable("unknown reflection kind"); +} + +bool template_of(APValue &Result, Sema &S, EvalFn Evaluator, QualType ResultTy, + SourceRange Range, ArrayRef Args) { + assert(Args[0]->getType()->isReflectionType()); + assert(ResultTy == S.Context.MetaInfoTy); + + APValue R; + if (!Evaluator(R, Args[0], true)) + return true; + + switch (R.getReflection().getKind()) { + case ReflectionValue::RK_type: { + TemplateName TName = findTemplateOfType(R.getReflectedType()); + if (TName.isNull()) + return true; + + return SetAndSucceed(Result, makeReflection(TName)); + } + case ReflectionValue::RK_declaration: { + TemplateName TName = findTemplateOfDecl(R.getReflectedDecl()); + if (TName.isNull()) + return true; + + return SetAndSucceed(Result, makeReflection(TName)); + } + case ReflectionValue::RK_const_value: + case ReflectionValue::RK_template: + case ReflectionValue::RK_namespace: + case ReflectionValue::RK_base_specifier: + case ReflectionValue::RK_data_member_spec: + return true; + } + llvm_unreachable("unknown reflection kind"); +} + +// TODO(P2996): Abstract this out, and use as an implementation detail of +// 'substitute'. +bool can_substitute(APValue &Result, Sema &S, EvalFn Evaluator, + QualType ResultTy, SourceRange Range, + ArrayRef Args) { + static auto CanActAsTemplateArg = [](const ReflectionValue &RV) -> bool { + switch (RV.getKind()) { + case ReflectionValue::RK_type: + case ReflectionValue::RK_declaration: + case ReflectionValue::RK_const_value: + case ReflectionValue::RK_template: + return true; + case ReflectionValue::RK_namespace: + case ReflectionValue::RK_base_specifier: + case ReflectionValue::RK_data_member_spec: + return false; + } + llvm_unreachable("unknown reflection kind"); + }; + + assert(Args[0]->getType()->isReflectionType()); + assert( + Args[1]->getType()->getPointeeOrArrayElementType()->isReflectionType()); + assert(Args[2]->getType()->isIntegerType()); + + APValue Template; + if (!Evaluator(Template, Args[0], true) || + Template.getReflection().getKind() != ReflectionValue::RK_template) + return true; + TemplateDecl *TDecl = Template.getReflectedTemplate().getAsTemplateDecl(); + + SmallVector TArgs; + { + // Evaluate how many template arguments were provided. + APValue NumArgs; + if (!Evaluator(NumArgs, Args[2], true)) + return true; + size_t nArgs = NumArgs.getInt().getExtValue(); + TArgs.reserve(nArgs); + + for (uint64_t k = 0; k < nArgs; ++k) { + llvm::APInt Idx(S.Context.getTypeSize(S.Context.getSizeType()), k, false); + Expr *IdxExpr = IntegerLiteral::Create(S.Context, Idx, + S.Context.getSizeType(), + Args[1]->getExprLoc()); + + ArraySubscriptExpr *SubscriptExpr = + new (S.Context) ArraySubscriptExpr(Args[1], IdxExpr, + S.Context.MetaInfoTy, + VK_LValue, OK_Ordinary, + Range.getBegin()); + + ImplicitCastExpr *RVExpr = ImplicitCastExpr::Create(S.Context, + S.Context.MetaInfoTy, + CK_LValueToRValue, + SubscriptExpr, + nullptr, VK_PRValue, + FPOptionsOverride()); + if (RVExpr->isValueDependent() || RVExpr->isTypeDependent()) + return true; + + APValue Unwrapped; + if (!Evaluator(Unwrapped, RVExpr, true) || !Unwrapped.isReflection() || + !CanActAsTemplateArg(Unwrapped.getReflection())) + return true; + + switch (Unwrapped.getReflection().getKind()) { + case ReflectionValue::RK_type: + TArgs.emplace_back(Unwrapped.getReflectedType().getCanonicalType()); + break; + case ReflectionValue::RK_const_value: { + ConstantExpr *E = Unwrapped.getReflectedConstValueExpr(); + if (E->getType()->isIntegralOrEnumerationType()) { + llvm::APSInt UnwrappedIntegral = E->EvaluateKnownConstInt(S.Context); + TArgs.emplace_back(S.Context, UnwrappedIntegral, + E->getType().getCanonicalType()); + } else if(E->getType()->isReflectionType()) { + APValue R; + if (!Evaluator(R, E, true)) + return true; + TArgs.emplace_back(S.Context, R.getReflection()); + } else { + TArgs.emplace_back(Unwrapped.getReflectedConstValueExpr()); + } + break; + } + case ReflectionValue::RK_declaration: { + ValueDecl *Decl = Unwrapped.getReflectedDecl(); + Expr *Synthesized = + DeclRefExpr::Create(S.Context, NestedNameSpecifierLoc(), + SourceLocation(), Decl, false, Range.getBegin(), + Decl->getType(), VK_LValue, Decl, nullptr); + APValue R; + if (!Evaluator(Unwrapped, Synthesized, true)) + return true; + + if (Synthesized->getType()->isIntegralOrEnumerationType()) + TArgs.emplace_back(S.Context, R.getInt(), + Synthesized->getType().getCanonicalType()); + else if(Synthesized->getType()->isReflectionType()) + TArgs.emplace_back(S.Context, R.getReflection()); + else + TArgs.emplace_back(Synthesized); + break; + } + case ReflectionValue::RK_template: + TArgs.emplace_back(Unwrapped.getReflectedTemplate()); + break; + default: + llvm_unreachable("unimplemented for template argument kind"); + } + } + } + + TemplateArgumentListInfo TAListInfo = + addLocToTemplateArgs(S, TArgs, Args[1]); + + SmallVector IgnoredCanonical; + SmallVector IgnoredSugared; + + { + Sema::SuppressDiagnosticsRAII NoDiagnostics(S); + bool CanSub = !S.CheckTemplateArgumentList(TDecl, Args[0]->getExprLoc(), + TAListInfo, false, + IgnoredSugared, IgnoredCanonical, + true); + return SetAndSucceed(Result, makeBool(S.Context, CanSub)); + } +} + +bool substitute(APValue &Result, Sema &S, EvalFn Evaluator, QualType ResultTy, + SourceRange Range, ArrayRef Args) { + static auto CanActAsTemplateArg = [](const ReflectionValue &RV) -> bool { + switch (RV.getKind()) { + case ReflectionValue::RK_type: + case ReflectionValue::RK_declaration: + case ReflectionValue::RK_const_value: + case ReflectionValue::RK_template: + return true; + case ReflectionValue::RK_namespace: + case ReflectionValue::RK_base_specifier: + case ReflectionValue::RK_data_member_spec: + return false; + } + llvm_unreachable("unknown reflection kind"); + }; + + assert(Args[0]->getType()->isReflectionType()); + assert( + Args[1]->getType()->getPointeeOrArrayElementType()->isReflectionType()); + assert(Args[2]->getType()->isIntegerType()); + + APValue Template; + if (!Evaluator(Template, Args[0], true) || + Template.getReflection().getKind() != ReflectionValue::RK_template) + return true; + TemplateDecl *TDecl = Template.getReflectedTemplate().getAsTemplateDecl(); + + SmallVector TArgs; + { + // Evaluate how many template arguments were provided. + APValue NumArgs; + if (!Evaluator(NumArgs, Args[2], true)) + return true; + size_t nArgs = NumArgs.getInt().getExtValue(); + TArgs.reserve(nArgs); + + for (uint64_t k = 0; k < nArgs; ++k) { + llvm::APInt Idx(S.Context.getTypeSize(S.Context.getSizeType()), k, false); + Expr *IdxExpr = IntegerLiteral::Create(S.Context, Idx, + S.Context.getSizeType(), + Args[1]->getExprLoc()); + + ArraySubscriptExpr *SubscriptExpr = + new (S.Context) ArraySubscriptExpr(Args[1], IdxExpr, + S.Context.MetaInfoTy, + VK_LValue, OK_Ordinary, + Range.getBegin()); + + ImplicitCastExpr *RVExpr = ImplicitCastExpr::Create(S.Context, + S.Context.MetaInfoTy, + CK_LValueToRValue, + SubscriptExpr, + nullptr, VK_PRValue, + FPOptionsOverride()); + if (RVExpr->isValueDependent() || RVExpr->isTypeDependent()) + return true; + + APValue Unwrapped; + if (!Evaluator(Unwrapped, RVExpr, true) || !Unwrapped.isReflection() || + !CanActAsTemplateArg(Unwrapped.getReflection())) + return true; + + switch (Unwrapped.getReflection().getKind()) { + case ReflectionValue::RK_type: + TArgs.emplace_back(Unwrapped.getReflectedType().getCanonicalType()); + break; + case ReflectionValue::RK_const_value: { + ConstantExpr *E = Unwrapped.getReflectedConstValueExpr(); + if (E->getType()->isIntegralOrEnumerationType()) { + llvm::APSInt UnwrappedIntegral = E->EvaluateKnownConstInt(S.Context); + TArgs.emplace_back(S.Context, UnwrappedIntegral, + E->getType().getCanonicalType()); + } else if(E->getType()->isReflectionType()) { + APValue R; + if (!Evaluator(R, E, true)) + return true; + TArgs.emplace_back(S.Context, R.getReflection()); + } else { + TArgs.emplace_back(Unwrapped.getReflectedConstValueExpr()); + } + break; + } + case ReflectionValue::RK_declaration: { + ValueDecl *Decl = Unwrapped.getReflectedDecl(); + Expr *Synthesized = + DeclRefExpr::Create(S.Context, NestedNameSpecifierLoc(), + SourceLocation(), Decl, false, Range.getBegin(), + Decl->getType(), VK_LValue, Decl, nullptr); + APValue R; + if (!Evaluator(Unwrapped, Synthesized, true)) + return true; + + if (Synthesized->getType()->isIntegralOrEnumerationType()) + TArgs.emplace_back(S.Context, R.getInt(), + Synthesized->getType().getCanonicalType()); + else if(Synthesized->getType()->isReflectionType()) + TArgs.emplace_back(S.Context, R.getReflection()); + else + TArgs.emplace_back(Synthesized); + break; + } + case ReflectionValue::RK_template: + TArgs.emplace_back(Unwrapped.getReflectedTemplate()); + break; + default: + llvm_unreachable("unimplemented for template argument kind"); + } + } + } + + { + TemplateArgumentListInfo TAListInfo = + addLocToTemplateArgs(S, TArgs, Args[1]); + + SmallVector CanonicalTArgs; + SmallVector IgnoredSugared; + if (S.CheckTemplateArgumentList(TDecl, Args[0]->getExprLoc(), TAListInfo, + false, IgnoredSugared, CanonicalTArgs, + true)) + return true; + TArgs = CanonicalTArgs; + } + + if (auto *CTD = dyn_cast(TDecl)) { + void *InsertPos; + ClassTemplateSpecializationDecl *TSpecDecl = + CTD->findSpecialization(TArgs, InsertPos); + + if (!TSpecDecl) { + TSpecDecl = ClassTemplateSpecializationDecl::Create( + S.Context, CTD->getTemplatedDecl()->getTagKind(), + CTD->getDeclContext(), Range.getBegin(), Range.getBegin(), + CTD, TArgs, nullptr); + CTD->AddSpecialization(TSpecDecl, InsertPos); + } + assert(TSpecDecl); + + APValue Value(ReflectionValue::RK_type, TSpecDecl->getTypeForDecl()); + return SetAndSucceed(Result, Value); + } else if (isa(TDecl)) { + TemplateArgumentListInfo TAListInfo = + addLocToTemplateArgs(S, TArgs, Args[1]); + + // TODO(P2996): Calling 'substitute' should not instantiate the alias + // template. Probably the logic for "substituting" the arguments into the + // template should be abstracted to a separate function. + QualType QT = S.CheckTemplateIdType(Template.getReflectedTemplate(), + Range.getBegin(), TAListInfo); + if (QT.isNull()) + return true; + + APValue Value(ReflectionValue::RK_type, QT.getAsOpaquePtr()); + return SetAndSucceed(Result, Value); + } else if (auto *FTD = dyn_cast(TDecl)) { + void *InsertPos; + FunctionDecl *TSpecDecl = FTD->findSpecialization(TArgs, InsertPos); + if (!TSpecDecl) { + TemplateArgumentList *TAList = TemplateArgumentList::CreateCopy(S.Context, + TArgs); + + // TODO(P2996): Calling 'substitute' should not instantiate the function + // template. Probably the logic for "substituting" the arguments into the + // template should be abstracted to a separate function. + TSpecDecl = S.InstantiateFunctionDeclaration(FTD, TAList, + Range.getBegin()); + } + if (!TSpecDecl) + // Could not instantiate function with the provided arguments. + return true; + + APValue Value(ReflectionValue::RK_declaration, TSpecDecl); + return SetAndSucceed(Result, Value); + } else if (auto *VTD = dyn_cast(TDecl)) { + void *InsertPos; + VarTemplateSpecializationDecl *TSpecDecl = + VTD->findSpecialization(TArgs, InsertPos); + + if (!TSpecDecl) { + TemplateArgumentListInfo TAListInfo = addLocToTemplateArgs(S, TArgs, + Args[1]); + + DeclResult DR = S.CheckVarTemplateId(VTD, Range.getBegin(), + Range.getBegin(), TAListInfo); + TSpecDecl = cast(DR.get()); + if (!TSpecDecl->getTemplateSpecializationKind()) + TSpecDecl->setTemplateSpecializationKind(TSK_ImplicitInstantiation); + } + assert(TSpecDecl); + + APValue Value(ReflectionValue::RK_declaration, TSpecDecl); + return SetAndSucceed(Result, Value); + } else if (auto *CD = dyn_cast(TDecl)) { + TemplateArgumentListInfo TAListInfo = addLocToTemplateArgs(S, TArgs, + Args[1]); + + CXXScopeSpec SS; + DeclarationNameInfo DNI(CD->getDeclName(), Range.getBegin()); + ExprResult ER = S.CheckConceptTemplateId(SS, Range.getBegin(), DNI, CD, CD, + &TAListInfo); + assert(ER.get()); + + APValue SatisfiesConcept; + if (!Evaluator(SatisfiesConcept, ER.get(), true)) + return true; + + ConstantExpr *CE = + ConstantExpr::CreateEmpty(S.Context, + ConstantResultStorageKind::APValue); + CE->setType(S.Context.BoolTy); + CE->setValueKind(VK_PRValue); + CE->SetResult(SatisfiesConcept, S.Context); + + APValue Value(ReflectionValue::RK_const_value, CE); + return SetAndSucceed(Result, Value); + } + llvm_unreachable("unimplemented for template kind"); +} + + +bool value_of(APValue &Result, Sema &S, EvalFn Evaluator, QualType ResultTy, + SourceRange Range, ArrayRef Args) { + assert(Args[0]->getType()->isReflectionType()); + assert(Args[1]->getType()->isReflectionType()); + + bool ReturnsLValue = false; + if (auto *LVRT = dyn_cast(ResultTy)) { + ReturnsLValue = true; + ResultTy = LVRT->getPointeeType(); + } + + APValue R; + if (!Evaluator(R, Args[1], true)) + return true; + + switch (R.getReflection().getKind()) { + case ReflectionValue::RK_const_value: { + Expr *Synthesized = R.getReflectedConstValueExpr(); + + if (auto *RD = dyn_cast_or_null( + Synthesized->getType()->getAsCXXRecordDecl()); + RD && RD->isLambda() && ResultTy->isPointerType()) { + TypeSourceInfo *TSI = S.Context.CreateTypeSourceInfo(ResultTy, 0); + ExprResult ER = S.BuildCStyleCastExpr(Range.getBegin(), TSI, + Range.getEnd(), Synthesized); + + if (ER.isInvalid()) + return true; + Synthesized = ER.get(); + } + + if (Synthesized->getType().getCanonicalType().getTypePtr() != + ResultTy.getCanonicalType().getTypePtr()) + return true; + + return !Evaluator(Result, Synthesized, true); + } + case ReflectionValue::RK_declaration: { + ValueDecl *Decl = dyn_cast(R.getReflectedDecl()); + if (!Decl) + return true; + ensureInstantiated(S, Decl, Args[1]->getSourceRange()); + + bool isLambda = false; + if (auto *RD = Decl->getType()->getAsCXXRecordDecl()) + isLambda = RD->isLambda(); + + Expr *Synthesized; + if (isa(Decl) && !isLambda) { + if (ResultTy.getCanonicalType().getTypePtr() != + Decl->getType().getCanonicalType().getTypePtr()) + return true; + + Synthesized = ValueOfLValueExpr::Create(S.Context, Range, ResultTy, + Decl); + } else if (ReturnsLValue) { + // Only variables may be returned as LValues. + return true; + } else { + NestedNameSpecifierLocBuilder NNSLocBuilder; + if (auto *ParentClsDecl = dyn_cast_or_null( + Decl->getDeclContext())) { + TypeSourceInfo *TSI = S.Context.CreateTypeSourceInfo( + QualType(ParentClsDecl->getTypeForDecl(), 0), 0); + NNSLocBuilder.Extend(S.Context, Range.getBegin(), TSI->getTypeLoc(), + Range.getBegin()); + } + + ExprValueKind VK = VK_LValue; + if (isa(Decl)) + VK = VK_PRValue; + + Synthesized = DeclRefExpr::Create(S.Context, NNSLocBuilder.getTemporary(), + SourceLocation(), Decl, false, + Range.getBegin(), Decl->getType(), VK, + Decl, nullptr); + + if (isa(Decl) || isa(Decl)) { + ExprResult ER = S.CreateBuiltinUnaryOp(Range.getBegin(), UO_AddrOf, + Synthesized, true); + if (ER.isInvalid()) + return true; + Synthesized = ER.get(); + } else if (isa(Decl)) { + Synthesized = ImplicitCastExpr::Create( + S.Context, Synthesized->getType(), CK_IntegralCast, Synthesized, + /*BasePath=*/nullptr, VK_PRValue, FPOptionsOverride()); + } else if (isLambda && ResultTy->isPointerType()) { + TypeSourceInfo *TSI = S.Context.CreateTypeSourceInfo(ResultTy, 0); + ExprResult ER = S.BuildCStyleCastExpr(Range.getBegin(), TSI, + Range.getEnd(), Synthesized); + if (ER.isInvalid()) + return true; + Synthesized = ER.get(); + } + } + + if (Synthesized->getType().getCanonicalType().getTypePtr() != + ResultTy.getCanonicalType().getTypePtr()) + return true; + + return !Evaluator(Result, Synthesized, !ReturnsLValue); + } + case ReflectionValue::RK_type: + case ReflectionValue::RK_template: + case ReflectionValue::RK_namespace: + case ReflectionValue::RK_base_specifier: + case ReflectionValue::RK_data_member_spec: + return true; + } + llvm_unreachable("invalid reflection type"); +} + +bool is_public(APValue &Result, Sema &S, EvalFn Evaluator, QualType ResultTy, + SourceRange Range, ArrayRef Args) { + assert(Args[0]->getType()->isReflectionType()); + assert(ResultTy == S.Context.BoolTy); + + APValue R; + if (!Evaluator(R, Args[0], true)) + return true; + + switch (R.getReflection().getKind()) { + case ReflectionValue::RK_type: { + const Decl *D = findTypeDecl(R.getReflectedType()); + if (!D) + return true; + + bool IsPublic = (D->getAccess() == AS_public); + return SetAndSucceed(Result, makeBool(S.Context, IsPublic)); + } + case ReflectionValue::RK_declaration: { + bool IsPublic = (R.getReflectedDecl()->getAccess() == AS_public); + return SetAndSucceed(Result, makeBool(S.Context, IsPublic)); + } + case ReflectionValue::RK_template: { + const Decl *D = R.getReflectedTemplate().getAsTemplateDecl(); + + bool IsPublic = (D->getAccess() == AS_public); + return SetAndSucceed(Result, makeBool(S.Context, IsPublic)); + } + case ReflectionValue::RK_base_specifier: { + CXXBaseSpecifier *Base = R.getReflectedBaseSpecifier(); + bool IsPublic = (Base->getAccessSpecifier() == AS_public); + return SetAndSucceed(Result, makeBool(S.Context, IsPublic)); + } + case ReflectionValue::RK_const_value: + case ReflectionValue::RK_data_member_spec: + case ReflectionValue::RK_namespace: + return SetAndSucceed(Result, makeBool(S.Context, false)); + } + llvm_unreachable("invalid reflection type"); +} + +bool is_protected(APValue &Result, Sema &S, EvalFn Evaluator, QualType ResultTy, + SourceRange Range, ArrayRef Args) { + assert(Args[0]->getType()->isReflectionType()); + assert(ResultTy == S.Context.BoolTy); + + APValue R; + if (!Evaluator(R, Args[0], true)) + return true; + + switch (R.getReflection().getKind()) { + case ReflectionValue::RK_type: { + const Decl *D = findTypeDecl(R.getReflectedType()); + if (!D) + return true; + + bool IsProtected = (D->getAccess() == AS_protected); + return SetAndSucceed(Result, makeBool(S.Context, IsProtected)); + } + case ReflectionValue::RK_declaration: { + bool IsProtected = (R.getReflectedDecl()->getAccess() == AS_protected); + return SetAndSucceed(Result, makeBool(S.Context, IsProtected)); + } + case ReflectionValue::RK_template: { + const Decl *D = R.getReflectedTemplate().getAsTemplateDecl(); + + bool IsProtected = (D->getAccess() == AS_protected); + return SetAndSucceed(Result, makeBool(S.Context, IsProtected)); + } + case ReflectionValue::RK_base_specifier: { + CXXBaseSpecifier *Base = R.getReflectedBaseSpecifier(); + bool IsProtected = (Base->getAccessSpecifier() == AS_protected); + return SetAndSucceed(Result, makeBool(S.Context, IsProtected)); + } + case ReflectionValue::RK_const_value: + case ReflectionValue::RK_data_member_spec: + case ReflectionValue::RK_namespace: + return SetAndSucceed(Result, makeBool(S.Context, false)); + } + llvm_unreachable("invalid reflection type"); +} + +bool is_private(APValue &Result, Sema &S, EvalFn Evaluator, QualType ResultTy, + SourceRange Range, ArrayRef Args) { + assert(Args[0]->getType()->isReflectionType()); + assert(ResultTy == S.Context.BoolTy); + + APValue R; + if (!Evaluator(R, Args[0], true)) + return true; + + switch (R.getReflection().getKind()) { + case ReflectionValue::RK_type: { + const Decl *D = findTypeDecl(R.getReflectedType()); + if (!D) + return true; + + bool IsPrivate = (D->getAccess() == AS_private); + return SetAndSucceed(Result, makeBool(S.Context, IsPrivate)); + } + case ReflectionValue::RK_declaration: { + bool IsPrivate = (R.getReflectedDecl()->getAccess() == AS_private); + return SetAndSucceed(Result, makeBool(S.Context, IsPrivate)); + } + case ReflectionValue::RK_template: { + const Decl *D = R.getReflectedTemplate().getAsTemplateDecl(); + + bool IsPrivate = (D->getAccess() == AS_private); + return SetAndSucceed(Result, makeBool(S.Context, IsPrivate)); + } + case ReflectionValue::RK_base_specifier: { + CXXBaseSpecifier *Base = R.getReflectedBaseSpecifier(); + bool IsPrivate = (Base->getAccessSpecifier() == AS_private); + return SetAndSucceed(Result, makeBool(S.Context, IsPrivate)); + } + case ReflectionValue::RK_const_value: + case ReflectionValue::RK_namespace: + case ReflectionValue::RK_data_member_spec: + return SetAndSucceed(Result, makeBool(S.Context, false)); + } + llvm_unreachable("invalid reflection type"); +} + +bool is_accessible(APValue &Result, Sema &S, EvalFn Evaluator, + QualType ResultTy, SourceRange Range, + ArrayRef Args) { + assert(Args[0]->getType()->isReflectionType()); + assert(ResultTy == S.Context.BoolTy); + + APValue R; + if (!Evaluator(R, Args[0], true)) + return true; + + APValue Scratch; + StackLocationExpr *SLE = StackLocationExpr::Create(S.Context, + SourceRange(), 1); + if (!Evaluator(Scratch, SLE, true) || !R.isReflection()) + return true; + ReflectionValue AccessedFrom = Scratch.getReflection(); + if (AccessedFrom.getKind() != ReflectionValue::RK_declaration) + return true; + + DeclContext *AccessDC = dyn_cast_or_null( + AccessedFrom.getAsDecl()); + if (!AccessDC) + AccessDC = S.CurContext; + assert(AccessDC); + + switch (R.getReflection().getKind()) { + case ReflectionValue::RK_type: { + NamedDecl *D = findTypeDecl(R.getReflectedType()); + if (!D) + return true; + bool Accessible = isAccessible(S, AccessDC, D); + return SetAndSucceed(Result, makeBool(S.Context, Accessible)); + } + case ReflectionValue::RK_declaration: { + bool Accessible = isAccessible(S, AccessDC, R.getReflectedDecl()); + return SetAndSucceed(Result, makeBool(S.Context, Accessible)); + } + case ReflectionValue::RK_template: { + TemplateName TName = R.getReflectedTemplate(); + bool Accessible = isAccessible(S, AccessDC, TName.getAsTemplateDecl()); + return SetAndSucceed(Result, makeBool(S.Context, Accessible)); + } + case ReflectionValue::RK_base_specifier: { + CXXBaseSpecifier *BaseSpec = R.getReflectedBaseSpecifier(); + + auto *Base = findTypeDecl(BaseSpec->getType()); + if (!Base) + return true; + + CXXBasePathElement bpe = { BaseSpec, BaseSpec->getDerived(), 0 }; + CXXBasePath path; + path.push_back(bpe); + path.Access = BaseSpec->getAccessSpecifier(); + + Sema::AccessResult AR; + DeclContext *PreviousDC = S.CurContext; + { + S.CurContext = AccessDC; + AR = S.CheckBaseClassAccess( + Range.getBegin(), BaseSpec->getType(), + QualType(BaseSpec->getDerived()->getTypeForDecl(), 0), path, 0, + /*ForceCheck=*/true, /*ForceUnprivileged=*/false); + S.CurContext = PreviousDC; + } + bool Accessible = (AR == Sema::AR_accessible); + return SetAndSucceed(Result, makeBool(S.Context, Accessible)); + } + case ReflectionValue::RK_const_value: + case ReflectionValue::RK_namespace: + case ReflectionValue::RK_data_member_spec: + return SetAndSucceed(Result, makeBool(S.Context, false)); + } + llvm_unreachable("invalid reflection type"); + return true; +} + +bool is_virtual(APValue &Result, Sema &S, EvalFn Evaluator, QualType ResultTy, + SourceRange Range, ArrayRef Args) { + assert(Args[0]->getType()->isReflectionType()); + assert(ResultTy == S.Context.BoolTy); + + APValue R; + if (!Evaluator(R, Args[0], true)) + return true; + + bool IsVirtual = false; + switch (R.getReflection().getKind()) { + case ReflectionValue::RK_declaration: { + if (const auto *MD = dyn_cast(R.getReflectedDecl())) + IsVirtual = MD->isVirtual(); + return SetAndSucceed(Result, makeBool(S.Context, IsVirtual)); + } + case ReflectionValue::RK_base_specifier: { + IsVirtual = R.getReflectedBaseSpecifier()->isVirtual(); + return SetAndSucceed(Result, makeBool(S.Context, IsVirtual)); + } + case ReflectionValue::RK_type: + case ReflectionValue::RK_const_value: + case ReflectionValue::RK_template: + case ReflectionValue::RK_namespace: + case ReflectionValue::RK_data_member_spec: + return SetAndSucceed(Result, makeBool(S.Context, IsVirtual)); + } + llvm_unreachable("invalid reflection type"); +} + +bool is_pure_virtual(APValue &Result, Sema &S, EvalFn Evaluator, + QualType ResultTy, SourceRange Range, + ArrayRef Args) { + assert(Args[0]->getType()->isReflectionType()); + assert(ResultTy == S.Context.BoolTy); + + APValue R; + if (!Evaluator(R, Args[0], true)) + return true; + + switch (R.getReflection().getKind()) { + case ReflectionValue::RK_type: + case ReflectionValue::RK_const_value: + case ReflectionValue::RK_template: + case ReflectionValue::RK_namespace: + case ReflectionValue::RK_base_specifier: + case ReflectionValue::RK_data_member_spec: + return SetAndSucceed(Result, makeBool(S.Context, false)); + case ReflectionValue::RK_declaration: { + if (const auto *FD = dyn_cast(R.getReflectedDecl())) + return SetAndSucceed(Result, makeBool(S.Context, FD->isPureVirtual())); + return true; + } + } + llvm_unreachable("invalid reflection type"); +} + +bool is_override(APValue &Result, Sema &S, EvalFn Evaluator, QualType ResultTy, + SourceRange Range, ArrayRef Args) { + assert(Args[0]->getType()->isReflectionType()); + assert(ResultTy == S.Context.BoolTy); + + APValue R; + if (!Evaluator(R, Args[0], true)) + return true; + + bool IsOverride = false; + switch (R.getReflection().getKind()) { + case ReflectionValue::RK_type: + case ReflectionValue::RK_const_value: + case ReflectionValue::RK_template: + case ReflectionValue::RK_namespace: + case ReflectionValue::RK_base_specifier: + case ReflectionValue::RK_data_member_spec: + return SetAndSucceed(Result, makeBool(S.Context, false)); + case ReflectionValue::RK_declaration: { + if (const auto *MD = dyn_cast(R.getReflectedDecl())) + IsOverride = MD->size_overridden_methods() > 0; + return SetAndSucceed(Result, makeBool(S.Context, IsOverride)); + } + } + llvm_unreachable("invalid reflection type"); +} + +bool is_deleted(APValue &Result, Sema &S, EvalFn Evaluator, QualType ResultTy, + SourceRange Range, ArrayRef Args) { + assert(Args[0]->getType()->isReflectionType()); + assert(ResultTy == S.Context.BoolTy); + + APValue R; + if (!Evaluator(R, Args[0], true)) + return true; + + switch (R.getReflection().getKind()) { + case ReflectionValue::RK_type: + case ReflectionValue::RK_const_value: + case ReflectionValue::RK_template: + case ReflectionValue::RK_namespace: + case ReflectionValue::RK_base_specifier: + case ReflectionValue::RK_data_member_spec: + return SetAndSucceed(Result, makeBool(S.Context, false)); + case ReflectionValue::RK_declaration: { + if (const auto *FD = dyn_cast(R.getReflectedDecl())) + return SetAndSucceed(Result, makeBool(S.Context, FD->isDeleted())); + return true; + } + } + llvm_unreachable("invalid reflection type"); +} + +bool is_defaulted(APValue &Result, Sema &S, EvalFn Evaluator, QualType ResultTy, + SourceRange Range, ArrayRef Args) { + assert(Args[0]->getType()->isReflectionType()); + assert(ResultTy == S.Context.BoolTy); + + APValue R; + if (!Evaluator(R, Args[0], true)) + return true; + + switch (R.getReflection().getKind()) { + case ReflectionValue::RK_type: + case ReflectionValue::RK_const_value: + case ReflectionValue::RK_template: + case ReflectionValue::RK_namespace: + case ReflectionValue::RK_base_specifier: + case ReflectionValue::RK_data_member_spec: + return SetAndSucceed(Result, makeBool(S.Context, false)); + case ReflectionValue::RK_declaration: { + if (const auto *FD = dyn_cast(R.getReflectedDecl())) + return SetAndSucceed(Result, makeBool(S.Context, FD->isDefaulted())); + return true; + } + } + llvm_unreachable("invalid reflection type"); +} + +bool is_explicit(APValue &Result, Sema &S, EvalFn Evaluator, QualType ResultTy, + SourceRange Range, ArrayRef Args) { + assert(Args[0]->getType()->isReflectionType()); + assert(ResultTy == S.Context.BoolTy); + + APValue R; + if (!Evaluator(R, Args[0], true)) + return true; + + switch (R.getReflection().getKind()) { + case ReflectionValue::RK_type: + case ReflectionValue::RK_const_value: + case ReflectionValue::RK_namespace: + case ReflectionValue::RK_base_specifier: + case ReflectionValue::RK_data_member_spec: + return SetAndSucceed(Result, makeBool(S.Context, false)); + case ReflectionValue::RK_template: { + TemplateDecl *TDecl = R.getReflectedTemplate().getAsTemplateDecl(); + if (auto *FTD = dyn_cast(TDecl)) + R = APValue(ReflectionValue::RK_declaration, FTD->getTemplatedDecl()); + else + return SetAndSucceed(Result, makeBool(S.Context, false)); + [[fallthrough]]; + } + case ReflectionValue::RK_declaration: { + ValueDecl *D = R.getReflectedDecl(); + + bool result = false; + if (auto *CtorD = dyn_cast(D)) + result = CtorD->getExplicitSpecifier().isExplicit(); + else if (auto *ConvD = dyn_cast(D)) + result = ConvD->getExplicitSpecifier().isExplicit(); + return SetAndSucceed(Result, makeBool(S.Context, result)); + } + } + llvm_unreachable("invalid reflection type"); +} + +bool is_bit_field(APValue &Result, Sema &S, EvalFn Evaluator, QualType ResultTy, + SourceRange Range, ArrayRef Args) { + assert(Args[0]->getType()->isReflectionType()); + assert(ResultTy == S.Context.BoolTy); + + APValue R; + if (!Evaluator(R, Args[0], true)) + return true; + + bool result = false; + if (R.getReflection().getKind() == ReflectionValue::RK_declaration) { + if (const auto *FD = dyn_cast(R.getReflectedDecl())) + result = FD->isBitField(); + } + return SetAndSucceed(Result, makeBool(S.Context, result)); +} + +bool has_static_storage_duration(APValue &Result, Sema &S, EvalFn Evaluator, + QualType ResultTy, SourceRange Range, + ArrayRef Args) { + assert(Args[0]->getType()->isReflectionType()); + assert(ResultTy == S.Context.BoolTy); + + APValue R; + if (!Evaluator(R, Args[0], true)) + return true; + + bool result = false; + if (R.getReflection().getKind() == ReflectionValue::RK_declaration) { + if (const auto *VD = dyn_cast(R.getReflectedDecl())) + result = VD->getStorageDuration() == SD_Static; + } + return SetAndSucceed(Result, makeBool(S.Context, result)); +} + +bool has_internal_linkage(APValue &Result, Sema &S, EvalFn Evaluator, + QualType ResultTy, SourceRange Range, + ArrayRef Args) { + assert(Args[0]->getType()->isReflectionType()); + assert(ResultTy == S.Context.BoolTy); + + APValue R; + if (!Evaluator(R, Args[0], true)) + return true; + + bool result = false; + if (R.getReflection().getKind() == ReflectionValue::RK_declaration) { + if (const auto *ND = dyn_cast(R.getReflectedDecl())) + result = (ND->getFormalLinkage() == Linkage::Internal); + } + return SetAndSucceed(Result, makeBool(S.Context, result)); +} + +bool has_external_linkage(APValue &Result, Sema &S, EvalFn Evaluator, + QualType ResultTy, SourceRange Range, + ArrayRef Args) { + assert(Args[0]->getType()->isReflectionType()); + assert(ResultTy == S.Context.BoolTy); + + APValue R; + if (!Evaluator(R, Args[0], true)) + return true; + + bool result = false; + if (R.getReflection().getKind() == ReflectionValue::RK_declaration) { + if (const auto *ND = dyn_cast(R.getReflectedDecl())) + result = (ND->getFormalLinkage() == Linkage::External); + } + return SetAndSucceed(Result, makeBool(S.Context, result)); +} + +bool has_linkage(APValue &Result, Sema &S, EvalFn Evaluator, QualType ResultTy, + SourceRange Range, ArrayRef Args) { + assert(Args[0]->getType()->isReflectionType()); + assert(ResultTy == S.Context.BoolTy); + + APValue R; + if (!Evaluator(R, Args[0], true)) + return true; + + bool result = false; + if (R.getReflection().getKind() == ReflectionValue::RK_declaration) { + if (const auto *ND = dyn_cast(R.getReflectedDecl())) + result = ND->hasLinkage(); + } + return SetAndSucceed(Result, makeBool(S.Context, result)); +} + +bool is_class_member(APValue &Result, Sema &S, EvalFn Evaluator, + QualType ResultTy, SourceRange Range, + ArrayRef Args) { + assert(Args[0]->getType()->isReflectionType()); + assert(ResultTy == S.Context.BoolTy); + + APValue Scratch; + + bool result = false; + if (!parent_of(Scratch, S, Evaluator, S.Context.MetaInfoTy, Range, Args)) { + assert(Scratch.isReflection()); + result = (Scratch.getReflection().getKind() == ReflectionValue::RK_type); + } + return SetAndSucceed(Result, makeBool(S.Context, result)); +} + +bool is_namespace_member(APValue &Result, Sema &S, EvalFn Evaluator, + QualType ResultTy, SourceRange Range, + ArrayRef Args) { + assert(Args[0]->getType()->isReflectionType()); + assert(ResultTy == S.Context.BoolTy); + + APValue Scratch; + + bool result = false; + if (!parent_of(Scratch, S, Evaluator, S.Context.MetaInfoTy, Range, Args)) { + assert(Scratch.isReflection()); + result = (Scratch.getReflection().getKind() == ReflectionValue::RK_namespace); + } + return SetAndSucceed(Result, makeBool(S.Context, result)); +} + +bool is_nonstatic_data_member(APValue &Result, Sema &S, EvalFn Evaluator, + QualType ResultTy, SourceRange Range, + ArrayRef Args) { + assert(Args[0]->getType()->isReflectionType()); + assert(ResultTy == S.Context.BoolTy); + + APValue R; + if (!Evaluator(R, Args[0], true)) + return true; + + bool result = false; + if (R.getReflection().getKind() == ReflectionValue::RK_declaration) { + result = isa(R.getReflectedDecl()); + } + return SetAndSucceed(Result, makeBool(S.Context, result)); +} + +bool is_static_member(APValue &Result, Sema &S, EvalFn Evaluator, + QualType ResultTy, SourceRange Range, + ArrayRef Args) { + assert(Args[0]->getType()->isReflectionType()); + assert(ResultTy == S.Context.BoolTy); + + APValue R; + if (!Evaluator(R, Args[0], true)) + return true; + + bool result = false; + switch (R.getReflection().getKind()) { + case ReflectionValue::RK_declaration: { + const ValueDecl *D = cast(R.getReflectedDecl()); + if (const auto *MD = dyn_cast(D)) + result = MD->isStatic(); + else if (const auto *VD = dyn_cast(D)) + result = VD->isStaticDataMember(); + return SetAndSucceed(Result, makeBool(S.Context, result)); + } + case ReflectionValue::RK_template: { + const Decl *D = R.getReflectedTemplate().getAsTemplateDecl(); + if (const auto *FTD = dyn_cast(D)) { + if (const auto *MD = dyn_cast(FTD->getTemplatedDecl())) + result = MD->isStatic(); + } else if (const auto *VTD = dyn_cast(D)) { + if (const auto *VD = dyn_cast(VTD->getTemplatedDecl())) + result = VD->isStaticDataMember(); + } + return SetAndSucceed(Result, makeBool(S.Context, result)); + } + case ReflectionValue::RK_type: + case ReflectionValue::RK_const_value: + case ReflectionValue::RK_namespace: + case ReflectionValue::RK_base_specifier: + case ReflectionValue::RK_data_member_spec: + return SetAndSucceed(Result, makeBool(S.Context, result)); + } + llvm_unreachable("unknown reflection kind"); +} + +bool is_base(APValue &Result, Sema &S, EvalFn Evaluator, QualType ResultTy, + SourceRange Range, ArrayRef Args) { + assert(Args[0]->getType()->isReflectionType()); + assert(ResultTy == S.Context.BoolTy); + + APValue R; + if (!Evaluator(R, Args[0], true)) + return true; + + return SetAndSucceed(Result, + makeBool(S.Context, + R.getReflection().getKind() == + ReflectionValue::RK_base_specifier)); +} + +bool is_namespace(APValue &Result, Sema &S, EvalFn Evaluator, QualType ResultTy, + SourceRange Range, ArrayRef Args) { + assert(Args[0]->getType()->isReflectionType()); + assert(ResultTy == S.Context.BoolTy); + + APValue R; + if (!Evaluator(R, Args[0], true)) + return true; + + return SetAndSucceed(Result, makeBool(S.Context, + R.getReflection().getKind() == + ReflectionValue::RK_namespace)); +} + +bool is_function(APValue &Result, Sema &S, EvalFn Evaluator, QualType ResultTy, + SourceRange Range, ArrayRef Args) { + assert(Args[0]->getType()->isReflectionType()); + assert(ResultTy == S.Context.BoolTy); + + APValue R; + if (!Evaluator(R, Args[0], true)) + return true; + + bool result = false; + if (R.getReflection().getKind() == ReflectionValue::RK_declaration) { + result = isa(R.getReflectedDecl()); + } + return SetAndSucceed(Result, makeBool(S.Context, result)); +} + +bool is_variable(APValue &Result, Sema &S, EvalFn Evaluator, QualType ResultTy, + SourceRange Range, ArrayRef Args) { + assert(Args[0]->getType()->isReflectionType()); + assert(ResultTy == S.Context.BoolTy); + + APValue R; + if (!Evaluator(R, Args[0], true)) + return true; + + bool result = false; + if (R.getReflection().getKind() == ReflectionValue::RK_declaration) { + result = isa(R.getReflectedDecl()); + } + return SetAndSucceed(Result, makeBool(S.Context, result)); +} + +bool is_type(APValue &Result, Sema &S, EvalFn Evaluator, QualType ResultTy, + SourceRange Range, ArrayRef Args) { + assert(Args[0]->getType()->isReflectionType()); + assert(ResultTy == S.Context.BoolTy); + + APValue R; + if (!Evaluator(R, Args[0], true)) + return true; + + return SetAndSucceed(Result, makeBool(S.Context, + R.getReflection().getKind() == + ReflectionValue::RK_type)); +} + +bool is_alias(APValue &Result, Sema &S, EvalFn Evaluator, QualType ResultTy, + SourceRange Range, ArrayRef Args) { + assert(Args[0]->getType()->isReflectionType()); + assert(ResultTy == S.Context.BoolTy); + + APValue R; + if (!Evaluator(R, Args[0], true)) + return true; + + switch (R.getReflection().getKind()) { + case ReflectionValue::RK_type: { + bool result = isTypeAlias(R.getReflectedType()); + return SetAndSucceed(Result, makeBool(S.Context, result)); + } + case ReflectionValue::RK_namespace: { + bool result = isa(R.getReflectedNamespace()); + return SetAndSucceed(Result, makeBool(S.Context, result)); + } + case ReflectionValue::RK_template: { + TemplateDecl *TDecl = R.getReflectedTemplate().getAsTemplateDecl(); + bool result = isa(TDecl); + return SetAndSucceed(Result, makeBool(S.Context, result)); + } + case ReflectionValue::RK_const_value: + case ReflectionValue::RK_declaration: + case ReflectionValue::RK_base_specifier: + case ReflectionValue::RK_data_member_spec: + return SetAndSucceed(Result, makeBool(S.Context, false)); + } + llvm_unreachable("unknown reflection kind"); +} + +bool is_incomplete_type(APValue &Result, Sema &S, EvalFn Evaluator, + QualType ResultTy, SourceRange Range, + ArrayRef Args) { + assert(Args[0]->getType()->isReflectionType()); + assert(ResultTy == S.Context.BoolTy); + + APValue R; + if (!Evaluator(R, Args[0], true)) + return true; + + bool result = false; + if (R.getReflection().getKind() == ReflectionValue::RK_type) { + result = R.getReflectedType()->isIncompleteType(); + } + return SetAndSucceed(Result, makeBool(S.Context, result)); +} + +bool is_template(APValue &Result, Sema &S, EvalFn Evaluator, QualType ResultTy, + SourceRange Range, ArrayRef Args) { + assert(Args[0]->getType()->isReflectionType()); + assert(ResultTy == S.Context.BoolTy); + + APValue R; + if (!Evaluator(R, Args[0], true)) + return true; + + return SetAndSucceed(Result, makeBool(S.Context, + R.getReflection().getKind() == + ReflectionValue::RK_template)); +} + +bool is_function_template(APValue &Result, Sema &S, EvalFn Evaluator, + QualType ResultTy, SourceRange Range, + ArrayRef Args) { + assert(Args[0]->getType()->isReflectionType()); + assert(ResultTy == S.Context.BoolTy); + + APValue R; + if (!Evaluator(R, Args[0], true)) + return true; + + bool IsFnTemplate = false; + if (R.getReflection().getKind() == ReflectionValue::RK_template) { + const TemplateDecl *TD = R.getReflectedTemplate().getAsTemplateDecl(); + IsFnTemplate = isa(TD); + } + return SetAndSucceed(Result, makeBool(S.Context, IsFnTemplate)); +} + +bool is_variable_template(APValue &Result, Sema &S, EvalFn Evaluator, + QualType ResultTy, SourceRange Range, + ArrayRef Args) { + assert(Args[0]->getType()->isReflectionType()); + assert(ResultTy == S.Context.BoolTy); + + APValue R; + if (!Evaluator(R, Args[0], true)) + return true; + + bool IsVarTemplate = false; + if (R.getReflection().getKind() == ReflectionValue::RK_template) { + const TemplateDecl *TD = R.getReflectedTemplate().getAsTemplateDecl(); + IsVarTemplate = isa(TD); + } + return SetAndSucceed(Result, makeBool(S.Context, IsVarTemplate)); +} + +bool is_class_template(APValue &Result, Sema &S, EvalFn Evaluator, + QualType ResultTy, SourceRange Range, + ArrayRef Args) { + assert(Args[0]->getType()->isReflectionType()); + assert(ResultTy == S.Context.BoolTy); + + APValue R; + if (!Evaluator(R, Args[0], true)) + return true; + + bool IsClsTemplate = false; + if (R.getReflection().getKind() == ReflectionValue::RK_template) { + const TemplateDecl *TD = R.getReflectedTemplate().getAsTemplateDecl(); + IsClsTemplate = isa(TD); + } + return SetAndSucceed(Result, makeBool(S.Context, IsClsTemplate)); +} + +bool is_alias_template(APValue &Result, Sema &S, EvalFn Evaluator, + QualType ResultTy, SourceRange Range, + ArrayRef Args) { + assert(Args[0]->getType()->isReflectionType()); + assert(ResultTy == S.Context.BoolTy); + + APValue R; + if (!Evaluator(R, Args[0], true)) + return true; + + bool IsAliasTemplate = false; + if (R.getReflection().getKind() == ReflectionValue::RK_template) { + const TemplateDecl *TD = R.getReflectedTemplate().getAsTemplateDecl(); + IsAliasTemplate = TD->isTypeAlias(); + } + return SetAndSucceed(Result, makeBool(S.Context, IsAliasTemplate)); +} + +bool is_concept(APValue &Result, Sema &S, EvalFn Evaluator, QualType ResultTy, + SourceRange Range, ArrayRef Args) { + assert(Args[0]->getType()->isReflectionType()); + assert(ResultTy == S.Context.BoolTy); + + APValue R; + if (!Evaluator(R, Args[0], true)) + return true; + + bool IsConcept = false; + if (R.getReflection().getKind() == ReflectionValue::RK_template) + IsConcept = isa(R.getReflectedTemplate().getAsTemplateDecl()); + + return SetAndSucceed(Result, makeBool(S.Context, IsConcept)); +} + +bool has_template_arguments(APValue &Result, Sema &S, EvalFn Evaluator, + QualType ResultTy, SourceRange Range, + ArrayRef Args) { + assert(Args[0]->getType()->isReflectionType()); + assert(ResultTy == S.Context.BoolTy); + + APValue R; + if (!Evaluator(R, Args[0], true)) + return true; + + switch (R.getReflection().getKind()) { + case ReflectionValue::RK_type: { + QualType QT = R.getReflectedType(); + bool result = isTemplateSpecialization(QT); + return SetAndSucceed(Result, makeBool(S.Context, result)); + } + case ReflectionValue::RK_declaration: { + bool result = false; + + Decl *D = R.getReflectedDecl(); + if (auto *FD = dyn_cast(D)) + result = (FD->getTemplateSpecializationArgs() != nullptr); + else if (auto *VTSD = dyn_cast(D)) + result = VTSD->getTemplateArgs().size() > 0; + + return SetAndSucceed(Result, makeBool(S.Context, result)); + } + case ReflectionValue::RK_const_value: + case ReflectionValue::RK_template: + case ReflectionValue::RK_namespace: + case ReflectionValue::RK_base_specifier: + case ReflectionValue::RK_data_member_spec: + return SetAndSucceed(Result, makeBool(S.Context, false)); + } + llvm_unreachable("unknown reflection kind"); +} + +bool is_constructor(APValue &Result, Sema &S, EvalFn Evaluator, + QualType ResultTy, SourceRange Range, + ArrayRef Args) { + assert(Args[0]->getType()->isReflectionType()); + assert(ResultTy == S.Context.BoolTy); + + APValue R; + if (!Evaluator(R, Args[0], true)) + return true; + + switch (R.getReflection().getKind()) { + case ReflectionValue::RK_type: + case ReflectionValue::RK_const_value: + case ReflectionValue::RK_namespace: + case ReflectionValue::RK_base_specifier: + case ReflectionValue::RK_data_member_spec: + return SetAndSucceed(Result, makeBool(S.Context, false)); + case ReflectionValue::RK_declaration: { + bool result = isa(R.getReflectedDecl()); + return SetAndSucceed(Result, makeBool(S.Context, result)); + } + case ReflectionValue::RK_template: { + bool result = false; + TemplateDecl *TDecl = R.getReflectedTemplate().getAsTemplateDecl(); + if (auto *FTD = dyn_cast(TDecl)) + result = isa(FTD->getTemplatedDecl()); + return SetAndSucceed(Result, makeBool(S.Context, result)); + } + } + llvm_unreachable("invalid reflection type"); +} + +bool is_destructor(APValue &Result, Sema &S, EvalFn Evaluator, + QualType ResultTy, SourceRange Range, + ArrayRef Args) { + assert(Args[0]->getType()->isReflectionType()); + assert(ResultTy == S.Context.BoolTy); + + APValue R; + if (!Evaluator(R, Args[0], true)) + return true; + + switch (R.getReflection().getKind()) { + case ReflectionValue::RK_type: + case ReflectionValue::RK_const_value: + case ReflectionValue::RK_template: + case ReflectionValue::RK_namespace: + case ReflectionValue::RK_base_specifier: + case ReflectionValue::RK_data_member_spec: + return SetAndSucceed(Result, makeBool(S.Context, false)); + case ReflectionValue::RK_declaration: { + bool result = isa(R.getReflectedDecl()); + return SetAndSucceed(Result, makeBool(S.Context, result)); + } + } + llvm_unreachable("invalid reflection type"); +} + +bool is_special_member(APValue &Result, Sema &S, EvalFn Evaluator, + QualType ResultTy, SourceRange Range, + ArrayRef Args) { + assert(Args[0]->getType()->isReflectionType()); + assert(ResultTy == S.Context.BoolTy); + + APValue R; + if (!Evaluator(R, Args[0], true)) + return true; + + switch (R.getReflection().getKind()) { + case ReflectionValue::RK_type: + case ReflectionValue::RK_const_value: + case ReflectionValue::RK_namespace: + case ReflectionValue::RK_base_specifier: + case ReflectionValue::RK_data_member_spec: + return SetAndSucceed(Result, makeBool(S.Context, false)); + case ReflectionValue::RK_declaration: { + bool IsSpecial = false; + if (auto *FD = dyn_cast(R.getReflectedDecl())) + IsSpecial = isSpecialMember(FD); + + return SetAndSucceed(Result, makeBool(S.Context, IsSpecial)); + } + case ReflectionValue::RK_template: { + bool result = false; + TemplateDecl *TDecl = R.getReflectedTemplate().getAsTemplateDecl(); + if (auto *FTD = dyn_cast(TDecl)) + result = isSpecialMember(FTD->getTemplatedDecl()); + return SetAndSucceed(Result, makeBool(S.Context, result)); + } + } + llvm_unreachable("invalid reflection type"); +} + +bool reflect_value(APValue &Result, Sema &S, EvalFn Evaluator, + QualType ResultTy, SourceRange Range, + ArrayRef Args) { + APValue Arg; + if (!Evaluator(Arg, Args[0], true)) + return true; + + ConstantExpr *E = + ConstantExpr::CreateEmpty(S.Context, + ConstantResultStorageKind::APValue); + E->setType(Args[0]->getType()); + E->setValueKind(VK_PRValue); + E->SetResult(Arg, S.Context); + + APValue Value(ReflectionValue::RK_const_value, E); + return SetAndSucceed(Result, Value); +} + +bool reflect_invoke(APValue &Result, Sema &S, EvalFn Evaluator, + QualType ResultTy, SourceRange Range, + ArrayRef Args) { + assert(Args[0]->getType()->isReflectionType()); + assert( + Args[1]->getType()->getPointeeOrArrayElementType()->isReflectionType()); + assert(Args[2]->getType()->isIntegerType()); + + SmallVector ArgExprs; + { + APValue NumArgs; + if (!Evaluator(NumArgs, Args[2], true)) + return true; + + size_t nArgs = NumArgs.getInt().getExtValue(); + ArgExprs.reserve(nArgs); + for (uint64_t k = 0; k < nArgs; ++k) { + llvm::APInt Idx(S.Context.getTypeSize(S.Context.getSizeType()), k, false); + Expr *IdxExpr = IntegerLiteral::Create(S.Context, Idx, + S.Context.getSizeType(), + Args[1]->getExprLoc()); + + ArraySubscriptExpr *SubscriptExpr = + new (S.Context) ArraySubscriptExpr(Args[1], IdxExpr, + S.Context.MetaInfoTy, + VK_LValue, OK_Ordinary, + Range.getBegin()); + + ImplicitCastExpr *RVExpr = ImplicitCastExpr::Create(S.Context, + S.Context.MetaInfoTy, + CK_LValueToRValue, + SubscriptExpr, + nullptr, VK_PRValue, + FPOptionsOverride()); + if (RVExpr->isValueDependent() || RVExpr->isTypeDependent()) + return true; + + APValue ArgResult; + if (!Evaluator(ArgResult, RVExpr, true) || + ArgResult.getReflection().getKind() != ReflectionValue::RK_const_value) + return true; + + Expr *CE = ArgResult.getReflectedConstValueExpr(); + ArgExprs.push_back(CE); + } + } + + APValue Scratch; + if (!Evaluator(Scratch, Args[0], true) || !Scratch.isReflection()) + return true; + + Expr *FnRefExpr = nullptr; + switch (Scratch.getReflection().getKind()) { + case ReflectionValue::RK_type: + case ReflectionValue::RK_namespace: + case ReflectionValue::RK_base_specifier: + case ReflectionValue::RK_data_member_spec: + return true; + case ReflectionValue::RK_const_value: + FnRefExpr = Scratch.getReflectedConstValueExpr(); + break; + case ReflectionValue::RK_declaration: { + ValueDecl *D = Scratch.getReflectedDecl(); + ensureInstantiated(S, D, Range); + + FnRefExpr = + DeclRefExpr::Create(S.Context, NestedNameSpecifierLoc(), + SourceLocation(), D, false, Range.getBegin(), + D->getType(), VK_LValue, D, nullptr); + break; + } + case ReflectionValue::RK_template: { + TemplateDecl *TDecl = Scratch.getReflectedTemplate().getAsTemplateDecl(); + auto *FTD = dyn_cast(TDecl); + if (!FTD) { + return true; + } + + FunctionDecl *Specialization; + { + sema::TemplateDeductionInfo Info(Args[0]->getExprLoc(), + FTD->getTemplateDepth()); + TemplateDeductionResult Result = S.DeduceTemplateArguments( + FTD, nullptr, ArgExprs, Specialization, Info, false, true, + QualType(), Expr::Classification(), + [](ArrayRef) { return false; }); + if (Result != TemplateDeductionResult::Success) + return true; + ensureInstantiated(S, Specialization, Range); + } + assert(Specialization && "no specialization found?"); + + FnRefExpr = + DeclRefExpr::Create(S.Context, NestedNameSpecifierLoc(), + SourceLocation(), Specialization, false, + Range.getBegin(), Specialization->getType(), + VK_LValue, Specialization, nullptr); + break; + } + } + + ExprResult ER; + if (auto *DRE = dyn_cast(FnRefExpr); + DRE && dyn_cast(DRE->getDecl())) { + auto *CtorD = cast(DRE->getDecl()); + ER = S.BuildCXXConstructExpr( + Range.getBegin(), QualType(CtorD->getParent()->getTypeForDecl(), 0), + CtorD, false, ArgExprs, false, false, false, false, + CXXConstructionKind::Complete, Range); + } else { + ER = S.ActOnCallExpr(S.getCurScope(), FnRefExpr, Range.getBegin(), ArgExprs, + Range.getEnd(), /*ExecConfig=*/nullptr); + } + if (ER.isInvalid()) + return true; + Expr *ResultExpr = ER.get(); + + if (ResultExpr->isTypeDependent() || ResultExpr->isValueDependent()) + return true; + + APValue FnResult; + if (!Evaluator(FnResult, ResultExpr, true)) + return true; + + ConstantExpr *CE = + ConstantExpr::CreateEmpty(S.Context, + ConstantResultStorageKind::APValue); + CE->setType(ResultExpr->getType()); + CE->setValueKind(VK_PRValue); + CE->SetResult(FnResult, S.Context); + + APValue Value(ReflectionValue::RK_const_value, CE); + return SetAndSucceed(Result, Value); +} + +bool data_member_spec(APValue &Result, Sema &S, EvalFn Evaluator, + QualType ResultTy, SourceRange Range, + ArrayRef Args) { + assert(Args[0]->getType()->isReflectionType()); + + APValue Scratch; + size_t ArgIdx = 0; + + // Extract the data member type. + if (!Evaluator(Scratch, Args[ArgIdx++], true) || + Scratch.getReflection().getKind() != ReflectionValue::RK_type) + return true; + QualType MemberTy = Scratch.getReflectedType(); + + // Evaluate whether the data member is static. + if (!Evaluator(Scratch, Args[ArgIdx++], true) || !Scratch.isInt()) + return true; + bool IsStatic = static_cast(Scratch.getInt().getExtValue()); + + // Evaluate whether a member name was provided. + std::optional Name; + if (!Evaluator(Scratch, Args[ArgIdx++], true)) + return true; + + // Evaluate the given name. Miserably inefficient, but gets the job done. + if (static_cast(Scratch.getInt().getExtValue())) { + // Evaluate 'name' length. + if (!Evaluator(Scratch, Args[ArgIdx++], true)) + return true; + size_t nameLen = Scratch.getInt().getExtValue(); + Name.emplace(nameLen, '\0'); + + for (uint64_t k = 0; k < nameLen; ++k) { + llvm::APInt Idx(S.Context.getTypeSize(S.Context.getSizeType()), k, false); + Expr *IdxExpr = IntegerLiteral::Create(S.Context, Idx, + S.Context.getSizeType(), + Args[ArgIdx]->getExprLoc()); + + ArraySubscriptExpr *SubscriptExpr = + new (S.Context) ArraySubscriptExpr(Args[ArgIdx], IdxExpr, + S.Context.CharTy, + VK_LValue, OK_Ordinary, + Range.getBegin()); + + ImplicitCastExpr *RVExpr = ImplicitCastExpr::Create(S.Context, + S.Context.CharTy, + CK_LValueToRValue, + SubscriptExpr, + nullptr, VK_PRValue, + FPOptionsOverride()); + if (RVExpr->isValueDependent() || RVExpr->isTypeDependent()) + return true; + + if (!Evaluator(Scratch, RVExpr, true)) + return true; + (*Name)[k] = static_cast(Scratch.getInt().getExtValue()); + } + ArgIdx++; + } else { + ArgIdx += 2; + } + + // Evaluate whether an alignment was provided. + std::optional Alignment; + if (!Evaluator(Scratch, Args[ArgIdx++], true)) + return true; + + if (static_cast(Scratch.getInt().getExtValue())) { + // Evaluate 'alignment' value. + if (!Evaluator(Scratch, Args[ArgIdx], true)) + return true; + int alignment = Scratch.getInt().getExtValue(); + + if (alignment < 0) + return true; + Alignment = static_cast(alignment); + } + ArgIdx++; + + // Evaluate whether a bit width was provided. + std::optional BitWidth; + if (!Evaluator(Scratch, Args[ArgIdx++], true)) + return true; + + if (static_cast(Scratch.getInt().getExtValue())) { + // Evaluate 'width' value. + if (!Evaluator(Scratch, Args[ArgIdx], true)) + return true; + int width = Scratch.getInt().getExtValue(); + + if (width < 0) + return true; + BitWidth = static_cast(width); + } + ArgIdx++; + + TagDataMemberSpec *TDMS = new (S.Context) TagDataMemberSpec { + MemberTy, Name, IsStatic, Alignment, BitWidth + }; + return SetAndSucceed(Result, makeReflection(TDMS)); +} + +bool define_class(APValue &Result, Sema &S, EvalFn Evaluator, QualType ResultTy, + SourceRange Range, ArrayRef Args) { + assert(Args[0]->getType()->isReflectionType()); + + APValue Scratch; + if (!Evaluator(Scratch, Args[0], true) || + Scratch.getReflection().getKind() != ReflectionValue::RK_type) + return true; + QualType ToComplete = Scratch.getReflectedType(); + TagDecl *OGTag, *Tag; + { + NamedDecl *ND; + if (!ToComplete->isIncompleteType(&ND)) + return true; + OGTag = Tag = cast(ND); + } + + unsigned TypeSpec = TST_struct; + if (Tag->getTagKind() == TagTypeKind::Class) + TypeSpec = TST_class; + else if (Tag->getTagKind() == TagTypeKind::Union) + TypeSpec = TST_union; + + TypeResult TR; + SmallVector MTP; + + DeclContext *OGDC = S.CurContext; + Scope ClsScope(S.getCurScope(), Scope::ClassScope | Scope::DeclScope, + S.Diags); + ClsScope.setEntity(OGTag->getDeclContext()); + + S.CurContext = OGTag->getDeclContext(); + auto RestoreDC = [&]() { S.CurContext = OGDC; }; + + CXXScopeSpec SS; + + bool OwnedDecl = true, IsDependent = false; + DeclResult DR; + if (auto *CTSD = dyn_cast(OGTag)) { + SmallVector CleanupList; + + TemplateName TDecl(CTSD->getSpecializedTemplate()); + ParsedTemplateTy ParsedTemplate = ParsedTemplateTy::make(TDecl); + + + SmallVector TArgs = expandTemplateArgPacks( + CTSD->getTemplateArgs().asArray()); + + SmallVector ParsedArgs; + for (const TemplateArgument &TArg : TArgs) { + switch (TArg.getKind()) { + case TemplateArgument::Type: + ParsedArgs.emplace_back(ParsedTemplateArgument::Type, + TArg.getAsType().getAsOpaquePtr(), + SourceLocation()); + break; + case TemplateArgument::Integral: { + ConstantExpr *CE = + ConstantExpr::CreateEmpty(S.Context, + ConstantResultStorageKind::APValue); + CE->setType(TArg.getIntegralType()); + CE->setValueKind(VK_PRValue); + CE->SetResult(APValue(TArg.getAsIntegral()), S.Context); + + ParsedArgs.emplace_back(ParsedTemplateArgument::NonType, CE, + SourceLocation()); + break; + } + case TemplateArgument::Template: { + ParsedTemplateTy Parsed = ParsedTemplateTy::make(TArg.getAsTemplate()); + ParsedArgs.emplace_back(SS, Parsed, SourceLocation()); + break; + } + default: + llvm_unreachable("unimplemented"); + } + } + + TemplateIdAnnotation *TAnnot = TemplateIdAnnotation::Create( + SourceLocation(), SourceLocation(), OGTag->getIdentifier(), OO_None, + ParsedTemplate, TNK_Type_template, SourceLocation(), SourceLocation(), + ParsedArgs, false, CleanupList); + + MTP.push_back(S.ActOnTemplateParameterList(0, SourceLocation(), + SourceLocation(), SourceLocation(), std::nullopt, + SourceLocation(), nullptr)); + DR = S.ActOnClassTemplateSpecialization( + &ClsScope, TypeSpec, Sema::TUK_Definition, Args[0]->getBeginLoc(), + SourceLocation(), SS, *TAnnot, ParsedAttributesView::none(), MTP, + nullptr); + MTP.clear(); + + for (auto *TAnnot : CleanupList) + TAnnot->Destroy(); + } else { + // If necessary, inject the tag declaration that is to be completed into the + // current scope. This is needed to ensure that the created Decl is + // constructed as a redeclaration of the provided incomplete Decl. + // + // A more robust design might allow 'ActOnTag' to take a 'PrevDecl' as an + // input, rather than require that it be found by name lookup. + bool InjectDecl = true; + for (Scope *Sc = S.getCurScope(); Sc; Sc = Sc->getParent()) + if (Sc->isDeclScope(OGTag)) { + InjectDecl = false; + break; + } + if (InjectDecl) { + S.getCurScope()->AddDecl(OGTag); + S.IdResolver.AddDecl(OGTag); + } + + // Create the new tag in the current scope. + DR = S.ActOnTag(S.getCurScope(), TypeSpec, Sema::TUK_Definition, + Args[0]->getBeginLoc(), SS, Tag->getIdentifier(), + Tag->getBeginLoc(), ParsedAttributesView::none(), + AS_none, SourceLocation(), MTP, OwnedDecl, IsDependent, + SourceLocation(), false, TR, false, false, + Sema::OOK_Outside, nullptr); + + // The new tag -should- declare the same entity as the original tag. + assert(declaresSameEntity(OGTag, Tag) && + "New tag should declare same entity as original tag (scope problem?)"); + } + if (DR.isInvalid()) { + RestoreDC(); + return true; + } + Tag = cast(DR.get()); + + S.ActOnTagStartDefinition(&ClsScope, Tag); + S.ActOnStartCXXMemberDeclarations(&ClsScope, Tag, SourceLocation(), + false, false, SourceLocation()); + + // Evaluate the number of members provided. + if (!Evaluator(Scratch, Args[1], true)) { + RestoreDC(); + return true; + } + size_t NumMembers = static_cast(Scratch.getInt().getExtValue()); + + AccessSpecifier MemberAS = (Tag->isClass() ? AS_private : AS_public); + + // Iterate over members, and add them to the class definition. + size_t anonMemCtr = 0; + for (size_t k = 0; k < NumMembers; ++k) { + // Extract the reflection from the list of member specs. + llvm::APInt Idx(S.Context.getTypeSize(S.Context.getSizeType()), k, false); + Expr *IdxExpr = IntegerLiteral::Create(S.Context, Idx, + S.Context.getSizeType(), + Args[2]->getExprLoc()); + + ArraySubscriptExpr *SubscriptExpr = + new (S.Context) ArraySubscriptExpr(Args[2], IdxExpr, + S.Context.MetaInfoTy, + VK_LValue, OK_Ordinary, + Range.getBegin()); + + ImplicitCastExpr *RVExpr = ImplicitCastExpr::Create(S.Context, + S.Context.MetaInfoTy, + CK_LValueToRValue, + SubscriptExpr, + nullptr, VK_PRValue, + FPOptionsOverride()); + if (RVExpr->isValueDependent() || RVExpr->isTypeDependent()) { + RestoreDC(); + return true; + } + + if (!Evaluator(Scratch, RVExpr, true) || + Scratch.getReflection().getKind() != + ReflectionValue::RK_data_member_spec) { + RestoreDC(); + return true; + } + TagDataMemberSpec *TDMS = Scratch.getReflectedDataMemberSpec(); + + + // Build the member declaration from the extracted TagDataMemberSpec. + unsigned DiagID; + const char *PrevSpec; + AttributeFactory AttrFactory; + AttributePool AttrPool(AttrFactory); + DeclSpec DS(AttrFactory); + DS.SetStorageClassSpec(S, TDMS->IsStatic ? DeclSpec::SCS_static : + DeclSpec::SCS_unspecified, + Args[0]->getBeginLoc(), + PrevSpec, DiagID, S.Context.getPrintingPolicy()); + + ParsedType MemberTy = ParsedType::make(TDMS->Ty); + DS.SetTypeSpecType(TST_typename, Args[0]->getBeginLoc(), PrevSpec, DiagID, + MemberTy, S.Context.getPrintingPolicy()); + + // Add any attributes (i.e., 'AlignAttr' if alignment is specified). + ParsedAttributesView MemberAttrs; + if (TDMS->Alignment) { + IdentifierInfo &AttrII = S.PP.getIdentifierTable().get("alignas"); + + ConstantExpr *CE = + ConstantExpr::CreateEmpty(S.Context, + ConstantResultStorageKind::APValue); + CE->setType(S.Context.getSizeType()); + CE->setValueKind(VK_PRValue); + CE->SetResult(APValue(llvm::APSInt::getUnsigned(TDMS->Alignment.value())), + S.Context); + + ArgsUnion Args(CE); + ParsedAttr::Form Form(tok::kw_alignas); + ParsedAttr *Attr = AttrPool.create(&AttrII, Range, nullptr, + SourceLocation(), &Args, 1, Form); + MemberAttrs.addAtEnd(Attr); + } + + Declarator MemberDeclarator(DS, MemberAttrs, DeclaratorContext::Member); + + std::string MemberName = TDMS->Name.value_or( + "__" + llvm::toString(llvm::APSInt::get(anonMemCtr++), 10)); + IdentifierInfo &II = S.PP.getIdentifierTable().get(MemberName); + MemberDeclarator.SetIdentifier(&II, Tag->getBeginLoc()); + + // Create an expression for bit width, if specified. + ConstantExpr *BitWidthCE = nullptr; + if (TDMS->BitWidth) { + BitWidthCE = + ConstantExpr::CreateEmpty(S.Context, + ConstantResultStorageKind::APValue); + BitWidthCE->setType(S.Context.getSizeType()); + BitWidthCE->setValueKind(VK_PRValue); + BitWidthCE->SetResult( + APValue(llvm::APSInt::getUnsigned(TDMS->BitWidth.value())), + S.Context); + } + + VirtSpecifiers VS; + S.ActOnCXXMemberDeclarator(&ClsScope, MemberAS, MemberDeclarator, MTP, + BitWidthCE, VS, ICIS_NoInit); + } + + S.ActOnFinishCXXMemberSpecification(&ClsScope, Tag->getBeginLoc(), Tag, + SourceLocation(), SourceLocation(), + ParsedAttributesView::none()); + + S.ActOnTagFinishDefinition(&ClsScope, Tag, Args[0]->getSourceRange()); + S.ActOnPopScope(Range.getEnd(), &ClsScope); + + RestoreDC(); + return SetAndSucceed(Result, makeReflection(ToComplete)); +} + +bool offset_of(APValue &Result, Sema &S, EvalFn Evaluator, QualType ResultTy, + SourceRange Range, ArrayRef Args) { + assert(Args[0]->getType()->isReflectionType()); + assert(ResultTy == S.Context.getSizeType()); + + APValue R; + if (!Evaluator(R, Args[0], true)) + return true; + + switch (R.getReflection().getKind()) { + case ReflectionValue::RK_type: + case ReflectionValue::RK_const_value: + case ReflectionValue::RK_template: + case ReflectionValue::RK_namespace: + case ReflectionValue::RK_base_specifier: + case ReflectionValue::RK_data_member_spec: + return true; + case ReflectionValue::RK_declaration: { + if (const FieldDecl *FD = dyn_cast(R.getReflectedDecl())) { + size_t Offset = getBitOffsetOfField(S.Context, FD) / + S.Context.getTypeSize(S.Context.CharTy); + return SetAndSucceed( + Result, + APValue(S.Context.MakeIntValue(Offset, S.Context.getSizeType()))); + } + return true; + } + } + llvm_unreachable("unknown reflection kind"); +} + +bool size_of(APValue &Result, Sema &S, EvalFn Evaluator, QualType ResultTy, + SourceRange Range, ArrayRef Args) { + assert(Args[0]->getType()->isReflectionType()); + assert(ResultTy == S.Context.getSizeType()); + + APValue R; + if (!Evaluator(R, Args[0], true)) + return true; + + switch (R.getReflection().getKind()) { + case ReflectionValue::RK_type: { + QualType QT = R.getReflectedType(); + size_t Sz = S.Context.getTypeSizeInChars(QT).getQuantity(); + return SetAndSucceed( + Result, + APValue(S.Context.MakeIntValue(Sz, S.Context.getSizeType()))); + } + case ReflectionValue::RK_const_value: { + const Expr *E = R.getReflectedConstValueExpr(); + size_t Sz = S.Context.getTypeSizeInChars(E->getType()).getQuantity(); + return SetAndSucceed( + Result, + APValue(S.Context.MakeIntValue(Sz, S.Context.getSizeType()))); + } + case ReflectionValue::RK_declaration: { + const ValueDecl *VD = cast(R.getReflectedDecl()); + size_t Sz = S.Context.getTypeSizeInChars(VD->getType()).getQuantity(); + return SetAndSucceed( + Result, + APValue(S.Context.MakeIntValue(Sz, S.Context.getSizeType()))); + } + case ReflectionValue::RK_template: + case ReflectionValue::RK_namespace: + case ReflectionValue::RK_base_specifier: + case ReflectionValue::RK_data_member_spec: + return true; + } + llvm_unreachable("unknown reflection kind"); +} + +bool bit_offset_of(APValue &Result, Sema &S, EvalFn Evaluator, + QualType ResultTy, SourceRange Range, + ArrayRef Args) { + assert(Args[0]->getType()->isReflectionType()); + assert(ResultTy == S.Context.getSizeType()); + + APValue R; + if (!Evaluator(R, Args[0], true)) + return true; + + switch (R.getReflection().getKind()) { + case ReflectionValue::RK_type: + case ReflectionValue::RK_const_value: + case ReflectionValue::RK_template: + case ReflectionValue::RK_namespace: + case ReflectionValue::RK_base_specifier: + case ReflectionValue::RK_data_member_spec: + return true; + case ReflectionValue::RK_declaration: { + if (const FieldDecl *FD = dyn_cast(R.getReflectedDecl())) { + size_t Offset = getBitOffsetOfField(S.Context, FD) % + S.Context.getTypeSize(S.Context.CharTy); + return SetAndSucceed( + Result, + APValue(S.Context.MakeIntValue(Offset, S.Context.getSizeType()))); + } + return true; + } + } + llvm_unreachable("unknown reflection kind"); +} + +bool bit_size_of(APValue &Result, Sema &S, EvalFn Evaluator, QualType ResultTy, + SourceRange Range, ArrayRef Args) { + assert(Args[0]->getType()->isReflectionType()); + assert(ResultTy == S.Context.getSizeType()); + + APValue R; + if (!Evaluator(R, Args[0], true)) + return true; + + switch (R.getReflection().getKind()) { + case ReflectionValue::RK_type: { + QualType QT = R.getReflectedType(); + size_t Sz = S.Context.getTypeSize(QT); + return SetAndSucceed( + Result, + APValue(S.Context.MakeIntValue(Sz, S.Context.getSizeType()))); + } + case ReflectionValue::RK_const_value: { + const Expr *E = R.getReflectedConstValueExpr(); + size_t Sz = S.Context.getTypeSize(E->getType()); + return SetAndSucceed( + Result, + APValue(S.Context.MakeIntValue(Sz, S.Context.getSizeType()))); + } + case ReflectionValue::RK_declaration: { + const ValueDecl *VD = cast(R.getReflectedDecl()); + size_t Sz = S.Context.getTypeSize(VD->getType()); + + if (const FieldDecl *FD = dyn_cast(VD)) + if (FD->isBitField()) + Sz = FD->getBitWidthValue(S.Context); + + return SetAndSucceed( + Result, + APValue(S.Context.MakeIntValue(Sz, S.Context.getSizeType()))); + } + case ReflectionValue::RK_template: + case ReflectionValue::RK_namespace: + case ReflectionValue::RK_base_specifier: + case ReflectionValue::RK_data_member_spec: + return true; + } + llvm_unreachable("unknown reflection kind"); +} + +bool alignment_of(APValue &Result, Sema &S, EvalFn Evaluator, + QualType ResultTy, SourceRange Range, + ArrayRef Args) { + assert(Args[0]->getType()->isReflectionType()); + assert(ResultTy == S.Context.getSizeType()); + + APValue R; + if (!Evaluator(R, Args[0], true)) + return true; + + switch (R.getReflection().getKind()) { + case ReflectionValue::RK_type: { + QualType QT = R.getReflectedType(); + size_t Align = S.Context.getTypeAlignInChars(QT).getQuantity(); + return SetAndSucceed( + Result, + APValue(S.Context.MakeIntValue(Align, S.Context.getSizeType()))); + } + case ReflectionValue::RK_const_value: { + const Expr *E = R.getReflectedConstValueExpr(); + size_t Align = S.Context.getTypeAlignInChars(E->getType()).getQuantity(); + return SetAndSucceed( + Result, + APValue(S.Context.MakeIntValue(Align, S.Context.getSizeType()))); + } + case ReflectionValue::RK_declaration: { + const ValueDecl *VD = cast(R.getReflectedDecl()); + size_t Align = S.Context.getTypeAlignInChars(VD->getType()).getQuantity(); + + if (const FieldDecl *FD = dyn_cast(VD)) + Align = S.Context.getDeclAlign(FD, true).getQuantity(); + + return SetAndSucceed( + Result, + APValue(S.Context.MakeIntValue(Align, S.Context.getSizeType()))); + } + case ReflectionValue::RK_template: + case ReflectionValue::RK_namespace: + case ReflectionValue::RK_base_specifier: + case ReflectionValue::RK_data_member_spec: + return true; + } + llvm_unreachable("unknown reflection kind"); +} + +bool get_ith_parameter_of(APValue &Result, Sema &S, EvalFn Evaluator, + QualType ResultTy, SourceRange Range, + ArrayRef Args) { + assert(Args[0]->getType()->isReflectionType()); + assert(ResultTy == S.Context.MetaInfoTy); + + APValue R; + if (!Evaluator(R, Args[0], true)) + return true; + + APValue Sentinel; + if (!Evaluator(Sentinel, Args[1], true)) + return true; + assert(Sentinel.getReflection().getKind() == ReflectionValue::RK_type); + + APValue Idx; + if (!Evaluator(Idx, Args[2], true)) + return true; + size_t idx = Idx.getInt().getExtValue(); + + switch (R.getReflection().getKind()) { + case ReflectionValue::RK_type: { + if (auto FT = dyn_cast(R.getReflectedType())) { + unsigned numParams = FT->getNumParams(); + if (idx >= numParams) + return SetAndSucceed(Result, Sentinel); + + return SetAndSucceed(Result, makeReflection(FT->getParamType(idx))); + } + return true; + } + case ReflectionValue::RK_declaration: { + if (auto FD = dyn_cast(R.getReflectedDecl())) { + unsigned numParams = FD->getNumParams(); + if (idx >= numParams) + return SetAndSucceed(Result, Sentinel); + + return SetAndSucceed(Result, makeReflection(FD->getParamDecl(idx))); + } + return true; + } + case ReflectionValue::RK_template: + case ReflectionValue::RK_const_value: + case ReflectionValue::RK_namespace: + case ReflectionValue::RK_base_specifier: + case ReflectionValue::RK_data_member_spec: + return true; + } + llvm_unreachable("unknown reflection kind"); +} + +bool has_unique_name(APValue &Result, Sema &S, EvalFn Evaluator, + QualType ResultTy, SourceRange Range, + ArrayRef Args) { + assert(Args[0]->getType()->isReflectionType()); + assert(ResultTy == S.Context.BoolTy); + + APValue R; + if (!Evaluator(R, Args[0], true)) + return true; + + switch (R.getReflection().getKind()) { + case ReflectionValue::RK_type: + case ReflectionValue::RK_const_value: + case ReflectionValue::RK_template: + case ReflectionValue::RK_namespace: + case ReflectionValue::RK_base_specifier: + case ReflectionValue::RK_data_member_spec: + return true; + case ReflectionValue::RK_declaration: { + if (auto *PVD = dyn_cast(R.getReflectedDecl())) { + StringRef FirstNameSeen = PVD->getName(); + ParmVarDecl *FirstParmSeen = PVD; + + bool Unique = true; + while (PVD) { + FunctionDecl *FD = cast(PVD->getDeclContext()); + FD = FD->getPreviousDecl(); + if (!FD) + break; + + PVD = FD->getParamDecl(FirstParmSeen->getFunctionScopeIndex()); + assert(PVD); + if (PVD->getName() != FirstNameSeen) { + Unique = false; + break; + } + } + return SetAndSucceed(Result, makeBool(S.Context, Unique)); + } + return true; + } + } + llvm_unreachable("unknown reflection kind"); +} + +bool has_default_argument(APValue &Result, Sema &S, EvalFn Evaluator, + QualType ResultTy, SourceRange Range, + ArrayRef Args) { + assert(Args[0]->getType()->isReflectionType()); + assert(ResultTy == S.Context.BoolTy); + + APValue R; + if (!Evaluator(R, Args[0], true)) + return true; + + switch (R.getReflection().getKind()) { + case ReflectionValue::RK_type: + case ReflectionValue::RK_const_value: + case ReflectionValue::RK_template: + case ReflectionValue::RK_namespace: + case ReflectionValue::RK_base_specifier: + case ReflectionValue::RK_data_member_spec: + return true; + case ReflectionValue::RK_declaration: { + if (auto *PVD = dyn_cast(R.getReflectedDecl())) { + return SetAndSucceed(Result, makeBool(S.Context, PVD->hasDefaultArg())); + } + return true; + } + } + llvm_unreachable("unknown reflection kind"); +} + + +} // end namespace clang diff --git a/clang/lib/Sema/Sema.cpp b/clang/lib/Sema/Sema.cpp index 04eadb5f3b8ae..3f5006446428f 100644 --- a/clang/lib/Sema/Sema.cpp +++ b/clang/lib/Sema/Sema.cpp @@ -210,7 +210,8 @@ Sema::Sema(Preprocessor &pp, ASTContext &ctxt, ASTConsumer &consumer, FpPragmaStack(FPOptionsOverride()), CurInitSeg(nullptr), VisContext(nullptr), PragmaAttributeCurrentTargetDecl(nullptr), StdCoroutineTraitsCache(nullptr), IdResolver(pp), - OriginalLexicalContext(nullptr), StdInitializerList(nullptr), + OriginalLexicalContext(nullptr), StdMetaNamespace(nullptr), + StdInitializerList(nullptr), FullyCheckedComparisonCategories( static_cast(ComparisonCategoryType::Last) + 1), StdSourceLocationImplDecl(nullptr), CXXTypeInfoDecl(nullptr), @@ -1849,6 +1850,9 @@ Sema::SemaDiagnosticBuilder::SemaDiagnosticBuilder(Kind K, SourceLocation Loc, Sema &S) : S(S), Loc(Loc), DiagID(DiagID), Fn(Fn), ShowCallStack(K == K_ImmediateWithCallStack || K == K_Deferred) { + if (S.SuppressDiagnostics) + K = K_Nop; + switch (K) { case K_Nop: break; diff --git a/clang/lib/Sema/SemaAccess.cpp b/clang/lib/Sema/SemaAccess.cpp index 4af3c0f30a8e8..e406bfec20053 100644 --- a/clang/lib/Sema/SemaAccess.cpp +++ b/clang/lib/Sema/SemaAccess.cpp @@ -1,5 +1,7 @@ //===---- SemaAccess.cpp - C++ Access Control -------------------*- C++ -*-===// // +// Copyright 2024 Bloomberg Finance L.P. +// // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception @@ -1557,7 +1559,7 @@ void Sema::HandleDependentAccessCheck(const DependentDiagnostic &DD, Sema::AccessResult Sema::CheckUnresolvedLookupAccess(UnresolvedLookupExpr *E, DeclAccessPair Found) { - if (!getLangOpts().AccessControl || + if (!languageAccessControl() || !E->getNamingClass() || Found.getAccess() == AS_public) return AR_accessible; @@ -1573,7 +1575,7 @@ Sema::AccessResult Sema::CheckUnresolvedLookupAccess(UnresolvedLookupExpr *E, /// access which has now been resolved to a member. Sema::AccessResult Sema::CheckUnresolvedMemberAccess(UnresolvedMemberExpr *E, DeclAccessPair Found) { - if (!getLangOpts().AccessControl || + if (!languageAccessControl() || Found.getAccess() == AS_public) return AR_accessible; @@ -1596,7 +1598,7 @@ bool Sema::isMemberAccessibleForDeletion(CXXRecordDecl *NamingClass, SourceLocation Loc, const PartialDiagnostic &Diag) { // Fast path. - if (Found.getAccess() == AS_public || !getLangOpts().AccessControl) + if (Found.getAccess() == AS_public || !languageAccessControl()) return true; AccessTarget Entity(Context, AccessTarget::Member, NamingClass, Found, @@ -1618,7 +1620,7 @@ Sema::AccessResult Sema::CheckDestructorAccess(SourceLocation Loc, CXXDestructorDecl *Dtor, const PartialDiagnostic &PDiag, QualType ObjectTy) { - if (!getLangOpts().AccessControl) + if (!languageAccessControl()) return AR_accessible; // There's never a path involved when checking implicit destructor access. @@ -1643,7 +1645,7 @@ Sema::AccessResult Sema::CheckConstructorAccess(SourceLocation UseLoc, DeclAccessPair Found, const InitializedEntity &Entity, bool IsCopyBindingRefToTemp) { - if (!getLangOpts().AccessControl || Found.getAccess() == AS_public) + if (!languageAccessControl() || Found.getAccess() == AS_public) return AR_accessible; PartialDiagnostic PD(PDiag()); @@ -1687,7 +1689,7 @@ Sema::AccessResult Sema::CheckConstructorAccess(SourceLocation UseLoc, DeclAccessPair Found, const InitializedEntity &Entity, const PartialDiagnostic &PD) { - if (!getLangOpts().AccessControl || + if (!languageAccessControl() || Found.getAccess() == AS_public) return AR_accessible; @@ -1729,7 +1731,7 @@ Sema::AccessResult Sema::CheckAllocationAccess(SourceLocation OpLoc, CXXRecordDecl *NamingClass, DeclAccessPair Found, bool Diagnose) { - if (!getLangOpts().AccessControl || + if (!languageAccessControl() || !NamingClass || Found.getAccess() == AS_public) return AR_accessible; @@ -1747,7 +1749,7 @@ Sema::AccessResult Sema::CheckAllocationAccess(SourceLocation OpLoc, Sema::AccessResult Sema::CheckMemberAccess(SourceLocation UseLoc, CXXRecordDecl *NamingClass, DeclAccessPair Found) { - if (!getLangOpts().AccessControl || + if (!languageAccessControl() || !NamingClass || Found.getAccess() == AS_public) return AR_accessible; @@ -1763,7 +1765,7 @@ Sema::AccessResult Sema::CheckStructuredBindingMemberAccess(SourceLocation UseLoc, CXXRecordDecl *DecomposedClass, DeclAccessPair Field) { - if (!getLangOpts().AccessControl || + if (!languageAccessControl() || Field.getAccess() == AS_public) return AR_accessible; @@ -1778,7 +1780,7 @@ Sema::AccessResult Sema::CheckMemberOperatorAccess(SourceLocation OpLoc, Expr *ObjectExpr, const SourceRange &Range, DeclAccessPair Found) { - if (!getLangOpts().AccessControl || Found.getAccess() == AS_public) + if (!languageAccessControl() || Found.getAccess() == AS_public) return AR_accessible; const RecordType *RT = ObjectExpr->getType()->castAs(); @@ -1822,8 +1824,7 @@ Sema::AccessResult Sema::CheckFriendAccess(NamedDecl *target) { // Friendship lookup is a redeclaration lookup, so there's never an // inheritance path modifying access. AccessSpecifier access = target->getAccess(); - - if (!getLangOpts().AccessControl || access == AS_public) + if (!languageAccessControl() || access == AS_public) return AR_accessible; CXXMethodDecl *method = cast(target->getAsFunction()); @@ -1849,7 +1850,7 @@ Sema::AccessResult Sema::CheckFriendAccess(NamedDecl *target) { Sema::AccessResult Sema::CheckAddressOfMemberAccess(Expr *OvlExpr, DeclAccessPair Found) { - if (!getLangOpts().AccessControl || + if (!languageAccessControl() || Found.getAccess() == AS_none || Found.getAccess() == AS_public) return AR_accessible; @@ -1878,7 +1879,7 @@ Sema::AccessResult Sema::CheckBaseClassAccess(SourceLocation AccessLoc, unsigned DiagID, bool ForceCheck, bool ForceUnprivileged) { - if (!ForceCheck && !getLangOpts().AccessControl) + if (!ForceCheck && !languageAccessControl()) return AR_accessible; if (Path.Access == AS_public) @@ -1907,7 +1908,7 @@ Sema::AccessResult Sema::CheckBaseClassAccess(SourceLocation AccessLoc, /// Checks access to all the declarations in the given result set. void Sema::CheckLookupAccess(const LookupResult &R) { - assert(getLangOpts().AccessControl + assert(languageAccessControl() && "performing access check without access control"); assert(R.getNamingClass() && "performing access check without naming class"); diff --git a/clang/lib/Sema/SemaCXXScopeSpec.cpp b/clang/lib/Sema/SemaCXXScopeSpec.cpp index fca5bd131bbc0..759f776d80a57 100644 --- a/clang/lib/Sema/SemaCXXScopeSpec.cpp +++ b/clang/lib/Sema/SemaCXXScopeSpec.cpp @@ -1,5 +1,7 @@ //===--- SemaCXXScopeSpec.cpp - Semantic Analysis for C++ scope specifiers-===// // +// Copyright 2024 Bloomberg Finance L.P. +// // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception @@ -163,8 +165,12 @@ DeclContext *Sema::computeDeclContext(const CXXScopeSpec &SS, case NestedNameSpecifier::Namespace: return NNS->getAsNamespace(); - case NestedNameSpecifier::NamespaceAlias: - return NNS->getAsNamespaceAlias()->getNamespace(); + case NestedNameSpecifier::NamespaceAlias: { + NamespaceAliasDecl *Alias = NNS->getAsNamespaceAlias(); + if (Alias->isDependent()) + return nullptr; + return Alias->getNamespace(); + } case NestedNameSpecifier::TypeSpec: case NestedNameSpecifier::TypeSpecWithTemplate: { @@ -178,6 +184,9 @@ DeclContext *Sema::computeDeclContext(const CXXScopeSpec &SS, case NestedNameSpecifier::Super: return NNS->getAsRecordDecl(); + + case NestedNameSpecifier::IndeterminateSplice: + return TryFindDeclContextOf(NNS->getAsSpliceExpr()); } llvm_unreachable("Invalid NestedNameSpecifier::Kind!"); @@ -500,6 +509,8 @@ bool Sema::BuildCXXNestedNameSpecifier(Scope *S, NestedNameSpecInfo &IdInfo, LookupCtx = computeDeclContext(SS, EnteringContext); isDependent = isDependentScopeSpecifier(SS); Found.setContextRange(SS.getRange()); + } else if (ScopeLookupResult && isa(ScopeLookupResult)) { + isDependent = true; } bool ObjectTypeSearchedInScope = false; @@ -1073,6 +1084,7 @@ bool Sema::ShouldEnterDeclaratorScope(Scope *S, const CXXScopeSpec &SS) { case NestedNameSpecifier::TypeSpec: case NestedNameSpecifier::TypeSpecWithTemplate: case NestedNameSpecifier::Super: + case NestedNameSpecifier::IndeterminateSplice: // These are never namespace scopes. return true; } diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp index c790dab72dd72..3e3fa18d1da1c 100644 --- a/clang/lib/Sema/SemaDecl.cpp +++ b/clang/lib/Sema/SemaDecl.cpp @@ -1,5 +1,7 @@ //===--- SemaDecl.cpp - Semantic Analysis for Declarations ----------------===// // +// Copyright 2024 Bloomberg Finance L.P. +// // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception diff --git a/clang/lib/Sema/SemaDeclCXX.cpp b/clang/lib/Sema/SemaDeclCXX.cpp index 068a2e4f04fa5..460928fe725d5 100644 --- a/clang/lib/Sema/SemaDeclCXX.cpp +++ b/clang/lib/Sema/SemaDeclCXX.cpp @@ -1,5 +1,7 @@ //===------ SemaDeclCXX.cpp - Semantic Analysis for C++ Declarations ------===// // +// Copyright 2024 Bloomberg Finance L.P. +// // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception @@ -2746,9 +2748,8 @@ Sema::CheckBaseSpecifier(CXXRecordDecl *Class, // emitted. if (!Class->getTypeForDecl()->isDependentType()) Class->setInvalidDecl(); - return new (Context) CXXBaseSpecifier( - SpecifierRange, Virtual, Class->getTagKind() == TagTypeKind::Class, - Access, TInfo, EllipsisLoc); + return new (Context) CXXBaseSpecifier(SpecifierRange, Virtual, Access, + TInfo, Class, EllipsisLoc); } // Base specifiers must be record types. @@ -2834,9 +2835,8 @@ Sema::CheckBaseSpecifier(CXXRecordDecl *Class, Class->setInvalidDecl(); // Create the base specifier. - return new (Context) CXXBaseSpecifier( - SpecifierRange, Virtual, Class->getTagKind() == TagTypeKind::Class, - Access, TInfo, EllipsisLoc); + return new (Context) CXXBaseSpecifier(SpecifierRange, Virtual, Access, + TInfo, Class, EllipsisLoc); } /// ActOnBaseSpecifier - Parsed a base specifier. A base specifier is @@ -11993,6 +11993,20 @@ NamespaceDecl *Sema::getOrCreateStdNamespace() { return getStdNamespace(); } +/// Retrieve the special "std::meta" namespace, if it's been defined. +NamespaceDecl *Sema::lookupStdMetaNamespace() { + if (!StdMetaNamespace) { + if (NamespaceDecl *Std = getStdNamespace()) { + LookupResult Result(*this, &PP.getIdentifierTable().get("meta"), + SourceLocation(), LookupNamespaceName); + if (!LookupQualifiedName(Result, Std) || + !(StdMetaNamespace = Result.getAsSingle())) + Result.suppressDiagnostics(); + } + } + return StdMetaNamespace; +} + bool Sema::isStdInitializerList(QualType Ty, QualType *Element) { assert(getLangOpts().CPlusPlus && "Looking for std::initializer_list outside of C++."); @@ -12206,6 +12220,28 @@ static bool TryNamespaceTypoCorrection(Sema &S, LookupResult &R, Scope *Sc, return false; } +Decl *Sema::ActOnNamespaceName(Scope *S, CXXScopeSpec &SS, IdentifierInfo *Id, + SourceLocation IdLoc) { + // Look up namespace name. + LookupResult R(*this, Id, IdLoc, LookupNamespaceName); + LookupParsedName(R, S, &SS); + if (R.isAmbiguous()) + return nullptr; + + if (R.empty() && !isReflectionContext()) + // Attempt a correction. + TryNamespaceTypoCorrection(*this, R, S, SS, IdLoc, Id); + + if (R.empty()) + return nullptr; + + if (isReflectionContext()) + if (auto *AD = R.getAsSingle()) + return AD; + + return R.getAsSingle(); +} + Decl *Sema::ActOnUsingDirective(Scope *S, SourceLocation UsingLoc, SourceLocation NamespcLoc, CXXScopeSpec &SS, SourceLocation IdentLoc, @@ -12223,6 +12259,12 @@ Decl *Sema::ActOnUsingDirective(Scope *S, SourceLocation UsingLoc, if (SS.isSet()) Qualifier = SS.getScopeRep(); + if (Qualifier && Qualifier->isDependent()) { + Diag(SS.getBeginLoc(), diag::err_using_dependent_namespace) + << SourceRange(SS.getBeginLoc(), IdentLoc); + return nullptr; + } + // Lookup namespace name. LookupResult R(*this, NamespcName, IdentLoc, LookupNamespaceName); LookupParsedName(R, S, &SS); @@ -12251,26 +12293,57 @@ Decl *Sema::ActOnUsingDirective(Scope *S, SourceLocation UsingLoc, // The use of a nested name specifier may trigger deprecation warnings. DiagnoseUseOfDecl(Named, IdentLoc); - // C++ [namespace.udir]p1: - // A using-directive specifies that the names in the nominated - // namespace can be used in the scope in which the - // using-directive appears after the using-directive. During - // unqualified name lookup (3.4.1), the names appear as if they - // were declared in the nearest enclosing namespace which - // contains both the using-directive and the nominated - // namespace. [Note: in this context, "contains" means "contains - // directly or indirectly". ] - - // Find enclosing context containing both using-directive and - // nominated namespace. - DeclContext *CommonAncestor = NS; - while (CommonAncestor && !CommonAncestor->Encloses(CurContext)) - CommonAncestor = CommonAncestor->getParent(); - - UDir = UsingDirectiveDecl::Create(Context, CurContext, UsingLoc, NamespcLoc, - SS.getWithLocInContext(Context), - IdentLoc, Named, CommonAncestor); + Decl *Result = ActOnUsingDirective(S, UsingLoc, NamespcLoc, SS, IdentLoc, Named, + NS, AttrList); + if (Result) + UDir = cast(Result); + } else { + Diag(IdentLoc, diag::err_expected_namespace_name) << SS.getRange(); + } + + return UDir; +} + +Decl *Sema::ActOnUsingDirective(Scope *S, SourceLocation UsingLoc, + SourceLocation NamespcLoc, CXXScopeSpec &SS, + SourceLocation IdentLoc, NamedDecl *Named, + NamespaceDecl *NS, + const ParsedAttributesView &AttrList) { + assert(!SS.isInvalid() && "Invalid CXXScopeSpec."); + assert(IdentLoc.isValid() && "Invalid NamespceName location."); + // Check for dependent namespaces. + if (auto *DNSD = dyn_cast(NS)) { + Diag(IdentLoc, diag::err_using_dependent_namespace) + << DNSD->getSpliceExpr()->getSourceRange(); + return nullptr; + } else if (auto *A = dyn_cast(NS); A && A->isDependent()) { + Diag(IdentLoc, diag::err_using_dependent_namespace) << IdentLoc; + return nullptr; + } + + // C++ [namespace.udir]p1: + // A using-directive specifies that the names in the nominated + // namespace can be used in the scope in which the + // using-directive appears after the using-directive. During + // unqualified name lookup (3.4.1), the names appear as if they + // were declared in the nearest enclosing namespace which + // contains both the using-directive and the nominated + // namespace. [Note: in this context, "contains" means "contains + // directly or indirectly". ] + + // Find enclosing context containing both using-directive and nominated + // namespace. + DeclContext *CommonAncestor = NS; + while (CommonAncestor && !CommonAncestor->Encloses(CurContext)) + CommonAncestor = CommonAncestor->getParent(); + + UsingDirectiveDecl *UDir = + UsingDirectiveDecl::Create(Context, CurContext, UsingLoc, NamespcLoc, + SS.getWithLocInContext(Context), + IdentLoc, Named, CommonAncestor); + + if (UDir) { if (IsUsingDirectiveInToplevelContext(CurContext) && !SourceMgr.isInMainFile(SourceMgr.getExpansionLoc(IdentLoc))) { Diag(IdentLoc, diag::warn_using_directive_in_header); @@ -12408,6 +12481,16 @@ Decl *Sema::ActOnUsingEnumDeclaration(Scope *S, AccessSpecifier AS, return nullptr; } + return ActOnUsingEnumDeclaration(S, AS, UsingLoc, EnumLoc, IdentLoc, EnumTy, + TSI); +} + +Decl *Sema::ActOnUsingEnumDeclaration(Scope *S, AccessSpecifier AS, + SourceLocation UsingLoc, + SourceLocation EnumLoc, + SourceLocation IdentLoc, + QualType EnumTy, + TypeSourceInfo *TSI) { auto *Enum = dyn_cast_if_present(EnumTy->getAsTagDecl()); if (!Enum) { Diag(IdentLoc, diag::err_using_enum_not_enum) << EnumTy; @@ -13681,23 +13764,45 @@ Decl *Sema::ActOnNamespaceAliasDef(Scope *S, SourceLocation NamespaceLoc, IdentifierInfo *Alias, CXXScopeSpec &SS, SourceLocation IdentLoc, IdentifierInfo *Ident) { + NamedDecl *ND; - // Lookup the namespace name. - LookupResult R(*this, Ident, IdentLoc, LookupNamespaceName); - LookupParsedName(R, S, &SS); + // Scope may be dependent if it has a splice as a leading component of its + // qualifiers, and that splice is dependent on a template parameter. + if (NestedNameSpecifier *NNS = SS.getScopeRep(); NNS && NNS->isDependent()) { + ND = NamespaceDecl::Create(Context, CurContext, false, IdentLoc, IdentLoc, + Ident, nullptr, true); + } else { + // Lookup the namespace name. + LookupResult R(*this, Ident, IdentLoc, LookupNamespaceName); - if (R.isAmbiguous()) - return nullptr; + if (S) { + LookupParsedName(R, S, &SS); + } else { + DeclContext *LookupCtx = computeDeclContext(SS, false); + LookupQualifiedName(R, LookupCtx); + } - if (R.empty()) { - if (!TryNamespaceTypoCorrection(*this, R, S, SS, IdentLoc, Ident)) { - Diag(IdentLoc, diag::err_expected_namespace_name) << SS.getRange(); + if (R.isAmbiguous()) return nullptr; + + if (R.empty()) { + if (!TryNamespaceTypoCorrection(*this, R, S, SS, IdentLoc, Ident)) { + Diag(IdentLoc, diag::err_expected_namespace_name) << SS.getRange(); + return nullptr; + } } + assert(!R.isAmbiguous() && !R.empty()); + ND = R.getRepresentativeDecl(); } - assert(!R.isAmbiguous() && !R.empty()); - NamedDecl *ND = R.getRepresentativeDecl(); + return ActOnNamespaceAliasDef(S, NamespaceLoc, AliasLoc, Alias, SS, IdentLoc, + ND); +} +Decl *Sema::ActOnNamespaceAliasDef(Scope *S, SourceLocation NamespaceLoc, + SourceLocation AliasLoc, + IdentifierInfo *Alias, CXXScopeSpec &SS, + SourceLocation IdentLoc, + NamedDecl *ND) { // Check if we have a previous declaration with the same name. LookupResult PrevR(*this, Alias, AliasLoc, LookupOrdinaryName, ForVisibleRedeclaration); @@ -13749,10 +13854,15 @@ Decl *Sema::ActOnNamespaceAliasDef(Scope *S, SourceLocation NamespaceLoc, if (Prev) AliasDecl->setPreviousDecl(Prev); - PushOnScopeChains(AliasDecl, S); + if (S) + PushOnScopeChains(AliasDecl, S); + else + CurContext->addDecl(AliasDecl); + return AliasDecl; } + namespace { struct SpecialMemberExceptionSpecInfo : SpecialMemberVisitor { diff --git a/clang/lib/Sema/SemaExceptionSpec.cpp b/clang/lib/Sema/SemaExceptionSpec.cpp index 00384f9dc16aa..f71e551793fbd 100644 --- a/clang/lib/Sema/SemaExceptionSpec.cpp +++ b/clang/lib/Sema/SemaExceptionSpec.cpp @@ -1391,6 +1391,13 @@ CanThrowResult Sema::canThrow(const Stmt *S) { case Expr::CXXNoexceptExprClass: case Expr::CXXNullPtrLiteralExprClass: case Expr::CXXPseudoDestructorExprClass: + case Expr::CXXReflectExprClass: + case Expr::CXXMetafunctionExprClass: + case Expr::CXXIndeterminateSpliceExprClass: + case Expr::CXXExprSpliceExprClass: + case Expr::CXXDependentMemberSpliceExprClass: + case Expr::StackLocationExprClass: + case Expr::ValueOfLValueExprClass: case Expr::CXXScalarValueInitExprClass: case Expr::CXXThisExprClass: case Expr::CXXUuidofExprClass: diff --git a/clang/lib/Sema/SemaExpr.cpp b/clang/lib/Sema/SemaExpr.cpp index 8db4fffeecfe3..8cdc766c79e09 100644 --- a/clang/lib/Sema/SemaExpr.cpp +++ b/clang/lib/Sema/SemaExpr.cpp @@ -1,5 +1,7 @@ //===--- SemaExpr.cpp - Semantic Analysis for Expressions -----------------===// // +// Copyright 2024 Bloomberg Finance L.P. +// // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception @@ -265,7 +267,7 @@ bool Sema::DiagnoseUseOfDecl(NamedDecl *D, ArrayRef Locs, if (FunctionDecl *FD = dyn_cast(D)) { // See if this is a deleted function. - if (FD->isDeleted()) { + if (FD->isDeleted() && !isReflectionContext()) { auto *Ctor = dyn_cast(FD); if (Ctor && Ctor->isInheritingConstructor()) Diag(Loc, diag::err_deleted_inherited_ctor_use) @@ -4744,6 +4746,9 @@ static void captureVariablyModifiedType(ASTContext &Context, QualType T, case Type::Decltype: T = cast(Ty)->desugar(); break; + case Type::ReflectionSplice: + T = cast(Ty)->desugar(); + break; case Type::PackIndexing: T = cast(Ty)->desugar(); break; @@ -13581,6 +13586,14 @@ QualType Sema::CheckCompareOperands(ExprResult &LHS, ExprResult &RHS, } } + // Reflection equality. + if (LHSType->isReflectionType() && RHSType->isReflectionType()) { + // Only == and != are defined for meta::info values. + if (!BinaryOperator::isEqualityOp(Opc)) + return InvalidOperands(Loc, LHS, RHS); + return computeResultTy(); + } + return InvalidOperands(Loc, LHS, RHS); } @@ -14998,6 +15011,8 @@ static ValueDecl *getPrimaryDecl(Expr *E) { return getPrimaryDecl(cast(E)->getSubExpr()); case Stmt::CXXUuidofExprClass: return cast(E)->getGuidDecl(); + case Stmt::CXXExprSpliceExprClass: + return getPrimaryDecl(cast(E)->getOperand()); default: return nullptr; } @@ -15024,6 +15039,7 @@ static void diagnoseAddressOfInvalidType(Sema &S, SourceLocation Loc, bool Sema::CheckUseOfCXXMethodAsAddressOfOperand(SourceLocation OpLoc, const Expr *Op, const CXXMethodDecl *MD) { + Op = Op->IgnoreExprSplices(); const auto *DRE = cast(Op->IgnoreParens()); if (Op != DRE) @@ -15099,6 +15115,7 @@ QualType Sema::CheckAddressOfOperand(ExprResult &OrigOp, SourceLocation OpLoc) { // Make sure to ignore parentheses in subsequent checks Expr *op = OrigOp.get()->IgnoreParens(); + // In OpenCL captures for blocks called as lambda functions // are located in the private address space. Blocks used in // enqueue_kernel can be located in a different address space @@ -15146,22 +15163,24 @@ QualType Sema::CheckAddressOfOperand(ExprResult &OrigOp, SourceLocation OpLoc) { } else if (isa(op)) { return Context.getPointerType(op->getType()); } else if (lval == Expr::LV_MemberFunction) { + Expr *unwrapped = op->IgnoreExprSplices(); // If it's an instance method, make a member pointer. // The expression must have exactly the form &A::foo. // If the underlying expression isn't a decl ref, give up. - if (!isa(op)) { + if (!isa(unwrapped)) { Diag(OpLoc, diag::err_invalid_form_pointer_member_function) << OrigOp.get()->getSourceRange(); return QualType(); } - DeclRefExpr *DRE = cast(op); + DeclRefExpr *DRE = cast(unwrapped); CXXMethodDecl *MD = cast(DRE->getDecl()); CheckUseOfCXXMethodAsAddressOfOperand(OpLoc, OrigOp.get(), MD); QualType MPTy = Context.getMemberPointerType( - op->getType(), Context.getTypeDeclType(MD->getParent()).getTypePtr()); + unwrapped->getType(), + Context.getTypeDeclType(MD->getParent()).getTypePtr()); // Under the MS ABI, lock down the inheritance model now. if (Context.getTargetInfo().getCXXABI().isMicrosoft()) (void)isCompleteType(OpLoc, MPTy); @@ -15207,10 +15226,12 @@ QualType Sema::CheckAddressOfOperand(ExprResult &OrigOp, SourceLocation OpLoc) { } else if (isa(dcl)) { return Context.OverloadTy; } else if (isa(dcl) || isa(dcl)) { + Expr *unwrapped = op->IgnoreExprSplices(); // Okay: we can take the address of a field. // Could be a pointer to member, though, if there is an explicit // scope qualifier for the class. - if (isa(op) && cast(op)->getQualifier()) { + if (isa(unwrapped) && + cast(unwrapped)->getQualifier()) { DeclContext *Ctx = dcl->getDeclContext(); if (Ctx && Ctx->isRecord()) { if (dcl->getType()->isReferenceType()) { @@ -15224,7 +15245,7 @@ QualType Sema::CheckAddressOfOperand(ExprResult &OrigOp, SourceLocation OpLoc) { Ctx = Ctx->getParent(); QualType MPTy = Context.getMemberPointerType( - op->getType(), + unwrapped->getType(), Context.getTypeDeclType(cast(Ctx)).getTypePtr()); // Under the MS ABI, lock down the inheritance model now. if (Context.getTargetInfo().getCXXABI().isMicrosoft()) @@ -17508,19 +17529,25 @@ ExprResult Sema::ActOnGNUNullExpr(SourceLocation TokenLoc) { return new (Context) GNUNullExpr(Ty, TokenLoc); } -static CXXRecordDecl *LookupStdSourceLocationImpl(Sema &S, SourceLocation Loc) { +RecordDecl *Sema::lookupStdSourceLocationImpl(SourceLocation Loc) { + if (StdSourceLocationImplDecl) + return StdSourceLocationImplDecl; + CXXRecordDecl *ImplDecl = nullptr; // Fetch the std::source_location::__impl decl. - if (NamespaceDecl *Std = S.getStdNamespace()) { - LookupResult ResultSL(S, &S.PP.getIdentifierTable().get("source_location"), - Loc, Sema::LookupOrdinaryName); - if (S.LookupQualifiedName(ResultSL, Std)) { + if (NamespaceDecl *Std = getStdNamespace()) { + AccessControlScopeGuard guard(*this, true); + + LookupResult ResultSL(*this, + &PP.getIdentifierTable().get("source_location"), Loc, + Sema::LookupOrdinaryName); + if (LookupQualifiedName(ResultSL, Std)) { if (auto *SLDecl = ResultSL.getAsSingle()) { - LookupResult ResultImpl(S, &S.PP.getIdentifierTable().get("__impl"), + LookupResult ResultImpl(*this, &PP.getIdentifierTable().get("__impl"), Loc, Sema::LookupOrdinaryName); if ((SLDecl->isCompleteDefinition() || SLDecl->isBeingDefined()) && - S.LookupQualifiedName(ResultImpl, SLDecl)) { + LookupQualifiedName(ResultImpl, SLDecl)) { ImplDecl = ResultImpl.getAsSingle(); } } @@ -17528,7 +17555,7 @@ static CXXRecordDecl *LookupStdSourceLocationImpl(Sema &S, SourceLocation Loc) { } if (!ImplDecl || !ImplDecl->isCompleteDefinition()) { - S.Diag(Loc, diag::err_std_source_location_impl_not_found); + Diag(Loc, diag::err_std_source_location_impl_not_found); return nullptr; } @@ -17536,7 +17563,7 @@ static CXXRecordDecl *LookupStdSourceLocationImpl(Sema &S, SourceLocation Loc) { // only the four expected fields. if (ImplDecl->isUnion() || !ImplDecl->isStandardLayout() || ImplDecl->getNumBases() != 0) { - S.Diag(Loc, diag::err_std_source_location_impl_malformed); + Diag(Loc, diag::err_std_source_location_impl_malformed); return nullptr; } @@ -17546,12 +17573,11 @@ static CXXRecordDecl *LookupStdSourceLocationImpl(Sema &S, SourceLocation Loc) { if (Name == "_M_file_name") { if (F->getType() != - S.Context.getPointerType(S.Context.CharTy.withConst())) + Context.getPointerType(Context.CharTy.withConst())) break; Count++; } else if (Name == "_M_function_name") { - if (F->getType() != - S.Context.getPointerType(S.Context.CharTy.withConst())) + if (F->getType() != Context.getPointerType(Context.CharTy.withConst())) break; Count++; } else if (Name == "_M_line") { @@ -17568,10 +17594,11 @@ static CXXRecordDecl *LookupStdSourceLocationImpl(Sema &S, SourceLocation Loc) { } } if (Count != 4) { - S.Diag(Loc, diag::err_std_source_location_impl_malformed); + Diag(Loc, diag::err_std_source_location_impl_malformed); return nullptr; } + StdSourceLocationImplDecl = ImplDecl; return ImplDecl; } @@ -17594,14 +17621,11 @@ ExprResult Sema::ActOnSourceLocExpr(SourceLocIdentKind Kind, ResultTy = Context.UnsignedIntTy; break; case SourceLocIdentKind::SourceLocStruct: - if (!StdSourceLocationImplDecl) { - StdSourceLocationImplDecl = - LookupStdSourceLocationImpl(*this, BuiltinLoc); - if (!StdSourceLocationImplDecl) - return ExprError(); - } + RecordDecl *ImplDecl = lookupStdSourceLocationImpl(BuiltinLoc); + if (!StdSourceLocationImplDecl) + return ExprError(); ResultTy = Context.getPointerType( - Context.getRecordType(StdSourceLocationImplDecl).withConst()); + Context.getRecordType(ImplDecl).withConst()); break; } @@ -18427,6 +18451,7 @@ ExprResult Sema::CheckForImmediateInvocation(ExprResult E, FunctionDecl *Decl) { /// evaluated until they are instantiated. if (!Res->isValueDependent()) ExprEvalContexts.back().ImmediateInvocationCandidates.emplace_back(Res, 0); + return Res; } @@ -18506,6 +18531,8 @@ static void RemoveNestedImmediateInvocation( if (!E->isImmediateInvocation()) return Base::TransformConstantExpr(E); RemoveImmediateInvocation(E); + if (!E->getSubExpr()) + return E; return Base::TransformExpr(E->getSubExpr()); } /// Base::TransfromCXXOperatorCallExpr doesn't traverse the callee so @@ -18779,6 +18806,7 @@ static bool isPotentiallyConstantEvaluatedContext(Sema &SemaRef) { case Sema::ExpressionEvaluationContext::Unevaluated: case Sema::ExpressionEvaluationContext::UnevaluatedAbstract: + case Sema::ExpressionEvaluationContext::ReflectionContext: // Expressions in this context are never evaluated. return false; } @@ -18882,6 +18910,7 @@ static OdrUseContext isOdrUseContext(Sema &SemaRef) { case Sema::ExpressionEvaluationContext::Unevaluated: case Sema::ExpressionEvaluationContext::UnevaluatedList: case Sema::ExpressionEvaluationContext::UnevaluatedAbstract: + case Sema::ExpressionEvaluationContext::ReflectionContext: return OdrUseContext::None; case Sema::ExpressionEvaluationContext::ConstantEvaluated: @@ -21020,6 +21049,7 @@ bool Sema::DiagRuntimeBehavior(SourceLocation Loc, ArrayRef Stmts, case ExpressionEvaluationContext::UnevaluatedList: case ExpressionEvaluationContext::UnevaluatedAbstract: case ExpressionEvaluationContext::DiscardedStatement: + case ExpressionEvaluationContext::ReflectionContext: // The argument will never be evaluated, so don't complain. break; diff --git a/clang/lib/Sema/SemaExprCXX.cpp b/clang/lib/Sema/SemaExprCXX.cpp index cfb5c6b6f2833..c0895bcd18fee 100644 --- a/clang/lib/Sema/SemaExprCXX.cpp +++ b/clang/lib/Sema/SemaExprCXX.cpp @@ -1,5 +1,7 @@ //===--- SemaExprCXX.cpp - Semantic Analysis for Expressions --------------===// // +// Copyright 2024 Bloomberg Finance L.P. +// // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception @@ -80,6 +82,7 @@ ParsedType Sema::getInheritingConstructorName(CXXScopeSpec &SS, case NestedNameSpecifier::Super: case NestedNameSpecifier::Namespace: case NestedNameSpecifier::NamespaceAlias: + case NestedNameSpecifier::IndeterminateSplice: llvm_unreachable("Nested name specifier is not a type for inheriting ctor"); } @@ -535,6 +538,7 @@ bool Sema::checkLiteralOperatorId(const CXXScopeSpec &SS, case NestedNameSpecifier::Super: case NestedNameSpecifier::Namespace: case NestedNameSpecifier::NamespaceAlias: + case NestedNameSpecifier::IndeterminateSplice: return false; } @@ -3321,7 +3325,7 @@ bool Sema::FindDeallocationFunction(SourceLocation StartLoc, CXXRecordDecl *RD, Operator = cast(Matches[0].FD); // FIXME: DiagnoseUseOfDecl? - if (Operator->isDeleted()) { + if (Operator->isDeleted() && !isReflectionContext()) { if (Diagnose) { Diag(StartLoc, diag::err_deleted_function_use); NoteDeletedFunction(Operator); diff --git a/clang/lib/Sema/SemaExprMember.cpp b/clang/lib/Sema/SemaExprMember.cpp index 32998ae60eafe..1820107848923 100644 --- a/clang/lib/Sema/SemaExprMember.cpp +++ b/clang/lib/Sema/SemaExprMember.cpp @@ -1,5 +1,7 @@ //===--- SemaExprMember.cpp - Semantic Analysis for Expressions -----------===// // +// Copyright 2024 Bloomberg Finance L.P. +// // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception @@ -76,7 +78,11 @@ enum IMAKind { /// All possible referrents are instance members of an unrelated /// class. - IMA_Error_Unrelated + IMA_Error_Unrelated, + + /// The reference is in the context of a reflection operand, which is allowed + /// because the context is unevaluated. + IMA_Reflection_Operand, }; /// The given lookup names class member(s) and is not being used for @@ -141,6 +147,9 @@ static IMAKind ClassifyImplicitMemberAccess(Sema &SemaRef, AbstractInstanceResult = IMA_Field_Uneval_Context; break; + case Sema::ExpressionEvaluationContext::ReflectionContext: + return IMA_Reflection_Operand; + case Sema::ExpressionEvaluationContext::UnevaluatedAbstract: AbstractInstanceResult = IMA_Abstract; break; @@ -282,6 +291,7 @@ ExprResult Sema::BuildPossibleImplicitMemberExpr( Diag(R.getNameLoc(), diag::warn_cxx98_compat_non_static_member_use) << R.getLookupNameInfo().getName(); [[fallthrough]]; + case IMA_Reflection_Operand: case IMA_Static: case IMA_Abstract: case IMA_Mixed_StaticOrExplicitContext: @@ -979,6 +989,15 @@ static bool IsInFnTryBlockHandler(const Scope *S) { return false; } +static bool isRecordType(QualType T) { + return T->isRecordType(); +} +static bool isPointerToRecordType(QualType T) { + if (const PointerType *PT = T->getAs()) + return PT->getPointeeType()->isRecordType(); + return false; +} + ExprResult Sema::BuildMemberReferenceExpr(Expr *BaseExpr, QualType BaseExprType, SourceLocation OpLoc, bool IsArrow, @@ -1214,6 +1233,91 @@ Sema::BuildMemberReferenceExpr(Expr *BaseExpr, QualType BaseExprType, return ExprError(); } +ExprResult +Sema::BuildMemberReferenceExpr(Scope *S, Expr *Base, SourceLocation OpLoc, + tok::TokenKind OpKind, CXXExprSpliceExpr *RHS, + SourceLocation TemplateKWLoc) { + // Disable access control for the duration of the splice expression + AccessControlScopeGuard guard {*this, true}; + + bool IsRHSDependent = (RHS->isValueDependent() || RHS->isTypeDependent()); + bool IsArrow = (OpKind == tok::arrow); + if (Base) { + const PointerType *PT = Base->getType()->getAs(); + bool IsPtr = (PT != nullptr); + bool IsRecord = isRecordType(PT ? PT->getPointeeType() : Base->getType()); + if (IsRecord && IsArrow != IsPtr) { + std::string Suggestion = IsPtr ? "." : "->"; + Diag(OpLoc, diag::err_typecheck_member_reference_suggestion) + << Base->getType() << int(IsArrow) << Base->getSourceRange() + << FixItHint::CreateReplacement(OpLoc, Suggestion); + return ExprError(); + } else if (!IsRecord && !IsRHSDependent) { + Diag(OpLoc, diag::err_typecheck_member_reference_struct_union) + << Base->getType() << Base->getSourceRange() + << RHS->getSourceRange(); + return ExprError(); + } + } + + if (IsRHSDependent) { + return BuildDependentMemberSpliceExpr(Base, OpLoc, IsArrow, RHS); + } + + CXXScopeSpec SS; + ValueDecl *VD = nullptr; + if (auto *DRE = dyn_cast(RHS->getOperand())) { + ValueDecl *D = DRE->getDecl(); + if (isa(D) || isa(D)) { + VD = D; + SS.Adopt(DRE->getQualifierLoc()); + } + } + if (!VD || (SS.isSet() && SS.isInvalid())) { + Diag(RHS->getExprLoc(), diag::err_member_access_splice_not_class_member); + return ExprError(); + } + + DeclarationNameInfo NameInfo(cast(VD)->getDeclName(), + VD->getLocation()); + LookupResult LR(*this, NameInfo, LookupMemberName); + if (LR.empty()) + LR.addDecl(VD); + + // Obnoxious translating of TemplateArgumentList to TemplateArgumentListInfo.. + TemplateArgumentListInfo TemplateArgs(RHS->getBeginLoc(), RHS->getEndLoc()); + if (const auto *FD = dyn_cast(VD)) { + if (const TemplateArgumentList *TAs = FD->getTemplateSpecializationArgs()) + for (const TemplateArgument &TA : TAs->asArray()) { + if (TA.getKind() == TemplateArgument::Type) { + QualType QT = TA.getAsType(); + TypeSourceInfo *TSI = Context.CreateTypeSourceInfo(QT, 0); + TemplateArgumentLoc TAL(TA, TSI); + TemplateArgs.addArgument(TAL); + } else { + TemplateArgumentLoc TAL(TA, TemplateArgumentLocInfo{}); + TemplateArgs.addArgument(TAL); + } + } + } + + ExprResult Res = BuildMemberReferenceExpr( + Base, Base->getType(), OpLoc, IsArrow, SS, TemplateKWLoc, nullptr, LR, + TemplateArgs.size() > 0 ? &TemplateArgs : 0, S, false, nullptr); + + if (!Res.isInvalid() && isa(Res.get())) + CheckMemberAccessOfNoDeref(cast(Res.get())); + + return Res; +} + +ExprResult +Sema::BuildDependentMemberSpliceExpr(Expr *Base, SourceLocation OpLoc, + bool IsArrow, CXXExprSpliceExpr *RHS) { + return CXXDependentMemberSpliceExpr::Create(Context, Base, OpLoc, IsArrow, + RHS); +} + /// Given that normal member access failed on the given expression, /// and given that the expression's type involves builtin-id or /// builtin-Class, decide whether substituting in the redefinition @@ -1246,15 +1350,6 @@ static bool ShouldTryAgainWithRedefinitionType(Sema &S, ExprResult &base) { return true; } -static bool isRecordType(QualType T) { - return T->isRecordType(); -} -static bool isPointerToRecordType(QualType T) { - if (const PointerType *PT = T->getAs()) - return PT->getPointeeType()->isRecordType(); - return false; -} - /// Perform conversions on the LHS of a member access expression. ExprResult Sema::PerformMemberExprBaseConversion(Expr *Base, bool IsArrow) { @@ -1799,6 +1894,16 @@ ExprResult Sema::ActOnMemberAccessExpr(Scope *S, Expr *Base, return Res; } +/// Variant of ActOnMemberAccessExpr used when the right-hand expression is an +/// expression splice (C++26, P2996) rather than an unqualified-id. +ExprResult Sema::ActOnMemberAccessExpr(Scope *S, Expr *Base, + SourceLocation OpLoc, + tok::TokenKind OpKind, + CXXExprSpliceExpr *RHS, + SourceLocation TemplateKWLoc) { + return BuildMemberReferenceExpr(S, Base, OpLoc, OpKind, RHS, TemplateKWLoc); +} + void Sema::CheckMemberAccessOfNoDeref(const MemberExpr *E) { if (isUnevaluatedContext()) return; diff --git a/clang/lib/Sema/SemaInit.cpp b/clang/lib/Sema/SemaInit.cpp index a75e9925a4314..5acf46b1f200c 100644 --- a/clang/lib/Sema/SemaInit.cpp +++ b/clang/lib/Sema/SemaInit.cpp @@ -7805,8 +7805,11 @@ static void visitLocalsRetainedByInitializer(IndirectLocalPath &Path, Init = DIE->getExpr(); } - if (auto *FE = dyn_cast(Init)) + if (auto *FE = dyn_cast(Init)) { Init = FE->getSubExpr(); + if (!Init) + return; + } // Dig out the expression which constructs the extended temporary. Init = const_cast(Init->skipRValueSubobjectAdjustments()); diff --git a/clang/lib/Sema/SemaLambda.cpp b/clang/lib/Sema/SemaLambda.cpp index 5b95bae567b72..79b14d37b1ed6 100644 --- a/clang/lib/Sema/SemaLambda.cpp +++ b/clang/lib/Sema/SemaLambda.cpp @@ -2198,6 +2198,7 @@ ExprResult Sema::BuildLambdaExpr(SourceLocation StartLoc, SourceLocation EndLoc, case ExpressionEvaluationContext::Unevaluated: case ExpressionEvaluationContext::UnevaluatedList: case ExpressionEvaluationContext::UnevaluatedAbstract: + case ExpressionEvaluationContext::ReflectionContext: // C++1y [expr.const]p2: // A conditional-expression e is a core constant expression unless the // evaluation of e, following the rules of the abstract machine, would diff --git a/clang/lib/Sema/SemaLookup.cpp b/clang/lib/Sema/SemaLookup.cpp index 38237ee578079..c6f513d469c4a 100644 --- a/clang/lib/Sema/SemaLookup.cpp +++ b/clang/lib/Sema/SemaLookup.cpp @@ -1,5 +1,7 @@ //===--------------------- SemaLookup.cpp - Name Lookup ------------------===// // +// Copyright 2024 Bloomberg Finance L.P. +// // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception @@ -446,6 +448,11 @@ static bool isPreferredLookupResult(Sema &S, Sema::LookupNameKind Kind, // For most kinds of declaration, it doesn't really matter which one we pick. if (!isa(DUnderlying) && !isa(DUnderlying)) { + // If this is in the context of taking a reflection and the existing + // declaration is a namespace alias, prefer the alias decl. + if (S.isReflectionContext() && isa(Existing)) + return true; + // If the existing declaration is hidden, prefer the new one. Otherwise, // keep what we've got. return !S.isVisible(Existing); @@ -2982,6 +2989,7 @@ addAssociatedClassesAndNamespaces(AssociatedLookup &Result, case TemplateArgument::Integral: case TemplateArgument::Expression: case TemplateArgument::NullPtr: + case TemplateArgument::Reflection: case TemplateArgument::StructuralValue: // [Note: non-type template arguments do not contribute to the set of // associated namespaces. ] @@ -3253,6 +3261,13 @@ addAssociatedClassesAndNamespaces(AssociatedLookup &Result, QualType Ty) { break; T = Queue.pop_back_val(); } + + if (T->isReflectionType()) { + NamespaceDecl *StdMeta = Result.S.lookupStdMetaNamespace(); + if (StdMeta) { + Result.Namespaces.insert(StdMeta); + } + } } /// Find the associated classes and namespaces for diff --git a/clang/lib/Sema/SemaOverload.cpp b/clang/lib/Sema/SemaOverload.cpp index 3808af37ff54a..c056c65aa404e 100644 --- a/clang/lib/Sema/SemaOverload.cpp +++ b/clang/lib/Sema/SemaOverload.cpp @@ -1,5 +1,7 @@ //===--- SemaOverload.cpp - C++ Overloading -------------------------------===// // +// Copyright 2024 Bloomberg Finance L.P. +// // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception @@ -13210,7 +13212,8 @@ class AddressOfFunctionResolver { bool IsInvalidFormOfPointerToMemberFunction() const { return TargetTypeIsNonStaticMemberFunction && - !OvlExprInfo.HasFormOfMemberPointer; + !OvlExprInfo.HasFormOfMemberPointer && + !S.isReflectionContext(); } void ComplainIsInvalidFormOfPointerToMemberFunction() const { diff --git a/clang/lib/Sema/SemaReflect.cpp b/clang/lib/Sema/SemaReflect.cpp new file mode 100644 index 0000000000000..fbeaa5feb6388 --- /dev/null +++ b/clang/lib/Sema/SemaReflect.cpp @@ -0,0 +1,775 @@ +//===--- SemaReflect.cpp - Semantic Analysis for Reflection ---------------===// +// +// Copyright 2024 Bloomberg Finance L.P. +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This file implements semantic analysis for reflection. +// +//===----------------------------------------------------------------------===// + +#include "TypeLocBuilder.h" +#include "clang/AST/DeclBase.h" +#include "clang/Basic/DiagnosticSema.h" +#include "clang/Sema/EnterExpressionEvaluationContext.h" +#include "clang/Sema/ParsedTemplate.h" +#include "clang/Sema/Metafunction.h" +#include "clang/Sema/Sema.h" + +using namespace clang; +using namespace sema; + +namespace { + +TemplateArgumentListInfo addLocToTemplateArgs(Sema &S, + ArrayRef Args, + SourceLocation ExprLoc) { + auto convert = [&](const TemplateArgument &TA) -> TemplateArgumentLoc { + return S.getTrivialTemplateArgumentLoc(TA, + TA.getNonTypeTemplateArgumentType(), + ExprLoc); + }; + + TemplateArgumentListInfo Result; + for (const TemplateArgument &Arg : Args) + if (Arg.getKind() == TemplateArgument::Pack) + for (const TemplateArgument &TA : Arg.getPackAsArray()) + Result.addArgument(convert(TA)); + else + Result.addArgument(convert(Arg)); + + return Result; +} + +Expr *CreateRefToDecl(Sema &S, ValueDecl *D, + SourceLocation ExprLoc) { + NestedNameSpecifierLocBuilder NNSLocBuilder; + if (const auto *RDC = dyn_cast(D->getDeclContext())) { + QualType QT(RDC->getTypeForDecl(), 0); + TypeSourceInfo *TSI = S.Context.CreateTypeSourceInfo(QT, 0); + NNSLocBuilder.Extend(S.Context, SourceLocation(), TSI->getTypeLoc(), + ExprLoc); + } + + ExprValueKind ValueKind = VK_LValue; + if (auto *VTSD = dyn_cast(D); + VTSD && VTSD->getTemplateSpecializationKind() == TSK_Undeclared) { + const TemplateArgumentList &TAList = VTSD->getTemplateArgs(); + TemplateArgumentListInfo TAListInfo( + addLocToTemplateArgs(S, TAList.asArray(), ExprLoc)); + + CXXScopeSpec SS; + DeclarationNameInfo DNI(VTSD->getDeclName(), ExprLoc); + ExprResult ER = S.CheckVarTemplateId(SS, DNI, + VTSD->getSpecializedTemplate(), + VTSD->getSpecializedTemplate(), + ExprLoc, &TAListInfo); + return ER.get(); + } else { + if (isa(D)) + ValueKind = VK_PRValue; + else if (auto *MD = dyn_cast(D); MD && !MD->isStatic()) + ValueKind = VK_PRValue; + + return DeclRefExpr::Create( + S.Context, NNSLocBuilder.getWithLocInContext(S.Context), + SourceLocation(), D, false, ExprLoc, D->getType(), ValueKind, D, + nullptr); + } +} +} // anonymous namespace + +ExprResult Sema::ActOnCXXReflectExpr(SourceLocation OpLoc, TypeResult T) { + ParsedTemplateArgument Arg = ActOnTemplateTypeArgument(T); + assert(Arg.getKind() == ParsedTemplateArgument::Type); + + return BuildCXXReflectExpr(OpLoc, Arg.getLocation(), T.get().get()); +} + +ExprResult Sema::ActOnCXXReflectExpr(SourceLocation OpLoc, Expr *E) { + return BuildCXXReflectExpr(OpLoc, E); +} + +ExprResult Sema::ActOnCXXReflectExpr(SourceLocation OpLoc, + SourceLocation ArgLoc, Decl *D) { + return BuildCXXReflectExpr(OpLoc, ArgLoc, D); +} + +ExprResult Sema::ActOnCXXReflectExpr(SourceLocation OpLoc, + ParsedTemplateArgument Template) { + assert(Template.getKind() == ParsedTemplateArgument::Template); + + ExprResult Result = BuildCXXReflectExpr(OpLoc, Template.getLocation(), + Template.getAsTemplate().get()); + if (!Result.isInvalid() && Template.getEllipsisLoc().isValid()) + Result = ActOnPackExpansion(Result.get(), Template.getEllipsisLoc()); + + return Result; +} + +ExprResult Sema::ActOnCXXReflectExpr(SourceLocation OpLoc, + CXXIndeterminateSpliceExpr *E) { + // P2996 does not permit diretly writing '^[:R:]' when 'R' is dependent. + if (E->isValueDependent()) { + Diag(E->getExprLoc(), diag::err_reflect_dependent_splice); + return ExprError(); + } + + Expr::EvalResult ER; + { + SmallVector Diags; + ER.Diag = &Diags; + + if (!E->EvaluateAsRValue(ER, Context, true)) { + Diag(E->getExprLoc(), diag::err_reflect_non_constexpr); + for (PartialDiagnosticAt PD : Diags) + Diag(PD.first, PD.second); + return ExprError(); + } + } + ReflectionValue &RV = ER.Val.getReflection(); + + switch (RV.getKind()) { + case ReflectionValue::RK_type: + return BuildCXXReflectExpr(OpLoc, E->getExprLoc(), RV.getAsType()); + case ReflectionValue::RK_const_value: + return BuildCXXReflectExpr(OpLoc, RV.getAsConstValueExpr()); + case ReflectionValue::RK_declaration: + return BuildCXXReflectExpr(OpLoc, E->getExprLoc(), RV.getAsDecl()); + case ReflectionValue::RK_template: + return BuildCXXReflectExpr(OpLoc, E->getExprLoc(), RV.getAsTemplate()); + case ReflectionValue::RK_namespace: + return BuildCXXReflectExpr(OpLoc, E->getExprLoc(), RV.getAsNamespace()); + case ReflectionValue::RK_base_specifier: + case ReflectionValue::RK_data_member_spec: + return ExprError(); + } + llvm_unreachable("unknown reflection kind"); +} + +/// Returns an expression representing the result of a metafunction operating +/// on a reflection. +ExprResult Sema::ActOnCXXMetafunction(SourceLocation KwLoc, + SourceLocation LParenLoc, + SmallVectorImpl &Args, + SourceLocation RParenLoc) { + if (Args.empty()) { + Diag(KwLoc, diag::err_metafunction_empty_args); + return ExprError(); + } + + // Extract and validate the metafunction ID. + Expr *FnIDArg = Args[0]; + { + if (FnIDArg->isTypeDependent() || FnIDArg->isValueDependent()) + return ExprError(); + + ExprResult FnIDArgConv = DefaultLvalueConversion(FnIDArg); + if (FnIDArgConv.isInvalid()) + return ExprError(); + + if (!FnIDArg->getType()->isIntegralOrEnumerationType()) { + Diag(FnIDArg->getExprLoc(), diag::err_metafunction_leading_arg_type); + return ExprError(); + } + Args[0] = FnIDArg = FnIDArgConv.get(); + } + + // Evaluate metafunction ID as an RValue. + Expr::EvalResult FnIDArgRV; + { + SmallVector Diags; + FnIDArgRV.Diag = &Diags; + + if (!FnIDArg->EvaluateAsRValue(FnIDArgRV, Context, true)) { + Diag(FnIDArg->getExprLoc(), diag::err_metafunction_not_constexpr); + for (PartialDiagnosticAt PD : Diags) + Diag(PD.first, PD.second); + return ExprError(); + } + } + unsigned FnID = static_cast(FnIDArgRV.Val.getInt().getExtValue()); + + // Look up the corresponding Metafunction object. + const Metafunction *Metafn; + if (Metafunction::Lookup(FnID, Metafn)) { + Diag(FnIDArg->getExprLoc(), diag::err_unknown_metafunction); + return ExprError(); + } + + // Validate the remaining arguments. + if (Args.size() < Metafn->getMinArgs() + 1 || + Args.size() > Metafn->getMaxArgs() + 1) { + Diag(KwLoc, diag::err_metafunction_arity) + << (Metafn->getMinArgs() + 1) + << (Metafn->getMaxArgs() + 1) + << Args.size(); + return ExprError(); + } + + // Find or build a 'std::function' having a lambda with the 'Sema' object + // (i.e., 'this') and the 'Metafunction' both captured. This will be provided + // as a callback to evaluate the metafunction at constant evaluation time. + auto ImplIt = MetafunctionImplCbs.find(FnID); + if (ImplIt == MetafunctionImplCbs.end()) { + auto MetafnImpl = + std::make_unique(std::function( + [this, Metafn]( + APValue &Result, CXXMetafunctionExpr::EvaluateFn EvalFn, + QualType ResultTy, SourceRange Range, + ArrayRef Args) -> bool { + return Metafn->evaluate(Result, *this, EvalFn, ResultTy, Range, + Args); + })); + ImplIt = MetafunctionImplCbs.try_emplace(FnID, std::move(MetafnImpl)).first; + } + + // Return the CXXMetafunctionExpr representation. + return BuildCXXMetafunctionExpr(KwLoc, LParenLoc, RParenLoc, + FnID, *ImplIt->second, Args); +} + +ExprResult Sema::ActOnCXXIndeterminateSpliceExpr(SourceLocation LSpliceLoc, + Expr *Operand, + SourceLocation RSpliceLoc) { + return BuildCXXIndeterminateSpliceExpr(LSpliceLoc, Operand, RSpliceLoc); +} + +TypeResult Sema::ActOnCXXSpliceExpectingType(SourceLocation LSpliceLoc, + Expr *Operand, + SourceLocation RSpliceLoc, + bool Complain) { + TypeLocBuilder TLB; + QualType SpliceTy = BuildReflectionSpliceTypeLoc(TLB, LSpliceLoc, Operand, + RSpliceLoc, Complain); + if (SpliceTy.isNull()) + return TypeError(); + return CreateParsedType(SpliceTy, TLB.getTypeSourceInfo(Context, SpliceTy)); +} + +ExprResult Sema::ActOnCXXSpliceExpectingExpr(SourceLocation LSpliceLoc, + Expr *Operand, + SourceLocation RSpliceLoc, + bool AllowMemberReference) { + return BuildReflectionSpliceExpr(LSpliceLoc, Operand, RSpliceLoc, + AllowMemberReference); +} + +DeclResult Sema::ActOnCXXSpliceExpectingNamespace(SourceLocation LSpliceLoc, + Expr *Operand, + SourceLocation RSpliceLoc) { + return BuildReflectionSpliceNamespace(LSpliceLoc, Operand, RSpliceLoc); +} + +ParsedTemplateArgument Sema::ActOnTemplateIndeterminateSpliceArgument( + CXXIndeterminateSpliceExpr *Splice) { + if (Splice->isValueDependent()) { + return ParsedTemplateArgument(ParsedTemplateArgument::IndeterminateSplice, + Splice, Splice->getExprLoc()); + } + + SmallVector Diags; + Expr::EvalResult ER; + ER.Diag = &Diags; + if (!Splice->EvaluateAsRValue(ER, Context, true)) { + Diag(Splice->getExprLoc(), diag::err_reflect_non_constexpr) + << Splice->getSourceRange(); + for (PartialDiagnosticAt PD : Diags) + Diag(PD.first, PD.second); + return ParsedTemplateArgument(); + } + if (ER.Val.getKind() != APValue::Reflection) { + // TODO(P2996): Replace with a diagnostic. + llvm_unreachable("expected a reflection"); + } + + ReflectionValue RV = ER.Val.getReflection(); + switch (RV.getKind()) { + case ReflectionValue::RK_type: + return ParsedTemplateArgument(ParsedTemplateArgument::Type, + RV.getAsType().getAsOpaquePtr(), + Splice->getExprLoc()); + case ReflectionValue::RK_const_value: + return ParsedTemplateArgument(ParsedTemplateArgument::NonType, + RV.getAsConstValueExpr(), + Splice->getExprLoc()); + case ReflectionValue::RK_template: { + TemplateName TName = RV.getAsTemplate(); + return ParsedTemplateArgument(ParsedTemplateArgument::Template, + TName.getAsTemplateDecl(), + Splice->getExprLoc()); + } + case ReflectionValue::RK_declaration: { + Expr *E = CreateRefToDecl(*this, cast(RV.getAsDecl()), + Splice->getExprLoc()); + return ParsedTemplateArgument(ParsedTemplateArgument::NonType, E, + E->getExprLoc()); + } + case ReflectionValue::RK_namespace: + Diag(Splice->getExprLoc(), diag::err_unsupported_splice_kind) + << "namespaces" << 0 << 0; + break; + case ReflectionValue::RK_base_specifier: + Diag(Splice->getExprLoc(), diag::err_unsupported_splice_kind) + << "base specifiers" << 0 << 0; + break; + case ReflectionValue::RK_data_member_spec: + Diag(Splice->getExprLoc(), diag::err_unsupported_splice_kind) + << "data member specs" << 0 << 0; + break; + } + return ParsedTemplateArgument(); +} + +bool Sema::ActOnCXXNestedNameSpecifierReflectionSplice( + CXXScopeSpec &SS, CXXIndeterminateSpliceExpr *Expr, + SourceLocation ColonColonLoc) { + assert(SS.isEmpty() && "splice must be leading component of NNS"); + + if (!Expr->isValueDependent() && !TryFindDeclContextOf(Expr)) + return true; + + SS.MakeIndeterminateSplice(Context, Expr, ColonColonLoc); + return false; +} + +ExprResult Sema::BuildCXXReflectExpr(SourceLocation OperatorLoc, + SourceLocation OperandLoc, QualType T) { + return CXXReflectExpr::Create(Context, OperatorLoc, OperandLoc, T); +} + +ExprResult Sema::BuildCXXReflectExpr(SourceLocation OperatorLoc, Expr *E) { + // Check if this is a reference to a declared entity. + if (auto *DRE = dyn_cast(E)) + return BuildCXXReflectExpr(OperatorLoc, DRE->getExprLoc(), DRE->getDecl()); + else if (auto *SNTTPE = dyn_cast(E)) + return BuildCXXReflectExpr(OperatorLoc, SNTTPE->getReplacement()); + + // Otherwise it must either be a constant expression or a dependent expression + // that cannot be evaluated before tree transform. + if (!E->isTypeDependent() && !E->isValueDependent() && + !isa(E)) { + // If this is an UnresolvedLookupExpr, the operand might be a specialized + // function template (which we can take the reflection of) or an overload + // set (which we cannot). Either way, handle this case separately. + if (UnresolvedLookupExpr *ULE = dyn_cast(E)) + return BuildCXXReflectExpr(OperatorLoc, ULE); + + SmallVector Diags; + Expr::EvalResult ER; + ER.Diag = &Diags; + + if (!E->EvaluateAsRValue(ER, Context, true)) { + Diag(E->getExprLoc(), diag::err_reflect_non_constexpr); + for (PartialDiagnosticAt PD : Diags) + Diag(PD.first, PD.second); + return ExprError(); + } + E = ConstantExpr::Create(Context, const_cast(E), ER.Val); + } + return CXXReflectExpr::Create(Context, OperatorLoc, E); +} + +ExprResult Sema::BuildCXXReflectExpr(SourceLocation OperatorLoc, + SourceLocation OperandLoc, Decl *D) { + return CXXReflectExpr::Create(Context, OperatorLoc, OperandLoc, D); +} + +ExprResult Sema::BuildCXXReflectExpr(SourceLocation OperatorLoc, + UnresolvedLookupExpr *E) { + // Entering ReflectionContext suppresses certain diagnostics from auto type + // deduction that can otherwise emit unhelpful errors. + EnterExpressionEvaluationContext EvalCtx( + *this, ExpressionEvaluationContext::ReflectionContext); + + // If the UnresolvedLookupExpr could refer to multiple candidates, there + // will be no means of choosing between them. Raise an error indicating + // lack of support for reflection of overload sets at this time. + auto *ULE = cast(E); + + // On the other hand, a unique candidate Decl might refer to a specialized + // function template. Begin by inventing a 'VarDecl' for a 'const auto' + // variable which would be initialized by the operand 'ULE'. + QualType ConstAutoTy = Context.getAutoDeductType().withConst(); + TypeSourceInfo *TSI = Context.CreateTypeSourceInfo(ConstAutoTy, 0); + auto *InventedVD = VarDecl::Create(Context, nullptr, SourceLocation(), + E->getExprLoc(), nullptr, ConstAutoTy, + TSI, SC_Auto); + + // Use the 'auto' deduction machinery to infer the operand type. + if (DeduceVariableDeclarationType(InventedVD, true, ULE)) { + Diag(E->getExprLoc(), diag::err_reflect_overload_set); + return ExprError(); + } + + // Now use the type to obtain the unique overload candidate that this can + // refer to; raise an error in the presence of any ambiguity. + bool HadMultipleCandidates; + DeclAccessPair FoundOverload; + FunctionDecl *FoundDecl = + ResolveAddressOfOverloadedFunction(ULE, InventedVD->getType(), true, + FoundOverload, + &HadMultipleCandidates); + if (!FoundDecl) { + Diag(E->getExprLoc(), diag::err_reflect_overload_set); + return ExprError(); + } + ExprResult ER = FixOverloadedFunctionReference(E, FoundOverload, FoundDecl); + assert(!ER.isInvalid() && "could not fix overloaded function reference"); + + return BuildCXXReflectExpr(OperatorLoc, ER.get()); +} + +ExprResult Sema::BuildCXXReflectExpr(SourceLocation OperatorLoc, + SourceLocation OperandLoc, + const TemplateName Template) { + if (Template.getKind() == TemplateName::OverloadedTemplate) { + Diag(OperandLoc, diag::err_reflect_overload_set); + return ExprError(); + } + + return CXXReflectExpr::Create(Context, OperatorLoc, OperandLoc, Template); +} + +ExprResult Sema::BuildCXXMetafunctionExpr( + SourceLocation KwLoc, SourceLocation LParenLoc, SourceLocation RParenLoc, + unsigned MetaFnID, const CXXMetafunctionExpr::ImplFn &Impl, + SmallVectorImpl &Args) { + // Look up the corresponding Metafunction object. + const Metafunction *MetaFn; + if (Metafunction::Lookup(MetaFnID, MetaFn)) { + Diag(Args[0]->getExprLoc(), diag::err_unknown_metafunction); + return ExprError(); + } + + // Derive the result type from the ResultKind of the metafunction. + auto DeriveResultTy = [&](QualType &Result) -> bool { + switch (MetaFn->getResultKind()) { + case Metafunction::MFRK_bool: + Result = Context.BoolTy; + return false; + case Metafunction::MFRK_cstring: + Result = Context.getPointerType(Context.getConstType(Context.CharTy)); + return false; + case Metafunction::MFRK_metaInfo: + Result = Context.MetaInfoTy; + return false; + case Metafunction::MFRK_sizeT: + Result = Context.getSizeType(); + return false; + case Metafunction::MFRK_sourceLoc: { + RecordDecl *SourceLocDecl = lookupStdSourceLocationImpl(KwLoc); + if (SourceLocDecl) + Result = Context.getPointerType( + Context.getRecordType(SourceLocDecl).withConst()); + return SourceLocDecl == nullptr; + } + case Metafunction::MFRK_spliceFromArg: { + Expr *TyRefl = Args[1]; + if (TyRefl->isTypeDependent() || TyRefl->isValueDependent()) { + Result = Context.DependentTy; + return false; + } + + SmallVector Diags; + Expr::EvalResult ER; + ER.Diag = &Diags; + + if (!TyRefl->EvaluateAsRValue(ER, Context, true)) { + Diag(TyRefl->getExprLoc(), diag::err_splice_operand_not_constexpr); + for (PartialDiagnosticAt PD : Diags) + Diag(PD.first, PD.second); + return true; + } + + if (!ER.Val.isReflection()) { + Diag(TyRefl->getExprLoc(), diag::err_splice_operand_not_reflection); + return true; + } + ReflectionValue& R = ER.Val.getReflection(); + + if (R.getKind() != ReflectionValue::RK_type) { + Diag(TyRefl->getExprLoc(), diag::err_unexpected_reflection_kind) << 0; + return true; + } + + Result = R.getAsType().getCanonicalType(); + return false; + } + } + llvm_unreachable("unknown metafunction result kind"); + }; + QualType ResultTy; + if (DeriveResultTy(ResultTy)) + return ExprError(); + return CXXMetafunctionExpr::Create(Context, MetaFnID, Impl, ResultTy, Args, + KwLoc, LParenLoc, RParenLoc); +} + +ExprResult Sema::BuildCXXIndeterminateSpliceExpr(SourceLocation LSpliceLoc, + Expr *Operand, + SourceLocation RSpliceLoc) { + ExprResult Result = DefaultLvalueConversion(Operand); + if (Result.isInvalid()) + return ExprError(); + Operand = Result.get(); + + if (!Operand->isValueDependent() && !Operand->isTypeDependent() && + Operand->getType() != Context.MetaInfoTy) { + Result = PerformImplicitConversion(Operand, Context.MetaInfoTy, + AA_Converting, false); + if (Result.isInvalid()) + return ExprError(); + Operand = Result.get(); + } + Operand = CXXIndeterminateSpliceExpr::Create(Context, LSpliceLoc, Operand, + RSpliceLoc); + + return Operand; +} + +QualType Sema::BuildReflectionSpliceType(SourceLocation LSplice, + Expr *Operand, + SourceLocation RSplice, + bool Complain) { + if (Operand->isTypeDependent() || Operand->isValueDependent()) { + return Context.getReflectionSpliceType(Operand, Context.DependentTy); + } + + SmallVector Diags; + Expr::EvalResult ER; + ER.Diag = &Diags; + + if (!Operand->EvaluateAsRValue(ER, Context, true)) { + Diag(Operand->getExprLoc(), diag::err_splice_operand_not_constexpr); + for (PartialDiagnosticAt PD : Diags) + Diag(PD.first, PD.second); + return QualType(); + } + + if (!ER.Val.isReflection()) { + Diag(Operand->getExprLoc(), diag::err_splice_operand_not_reflection); + return QualType(); + } + ReflectionValue &R = ER.Val.getReflection(); + + if (R.getKind() != ReflectionValue::RK_type) { + if (Complain) + Diag(Operand->getExprLoc(), diag::err_unexpected_reflection_kind) << 0; + return QualType(); + } + + QualType ReflectedTy = R.getAsType(); + + // Check if the type refers to a substituted but uninstantiated template. + if (auto *TT = dyn_cast(ReflectedTy)) + if (auto *CTD = dyn_cast(TT->getDecl()); + CTD && CTD->getSpecializationKind() == TSK_Undeclared) { + TemplateName TName(CTD->getSpecializedTemplate()); + + const TemplateArgumentList &TAList = + CTD->getTemplateInstantiationArgs(); + TemplateArgumentListInfo TAListInfo( + addLocToTemplateArgs(*this, TAList.asArray(), + Operand->getExprLoc())); + + ReflectedTy = CheckTemplateIdType(TName, Operand->getExprLoc(), + TAListInfo); + if (ReflectedTy.isNull()) + return QualType(); + } + + return Context.getReflectionSpliceType(Operand, ReflectedTy); +} + +QualType Sema::BuildReflectionSpliceTypeLoc(TypeLocBuilder &TLB, + SourceLocation LSpliceLoc, + Expr *E, + SourceLocation RSpliceLoc, + bool Complain) { + QualType SpliceTy = BuildReflectionSpliceType(LSpliceLoc, E, RSpliceLoc, + Complain); + if (SpliceTy.isNull()) + return QualType(); + else if (isa(SpliceTy)) { + auto TL = TLB.push(SpliceTy); + TL.setTemplateNameLoc(LSpliceLoc); + return SpliceTy; + } + + auto TL = TLB.push(SpliceTy); + TL.setLSpliceLoc(LSpliceLoc); + TL.setRSpliceLoc(RSpliceLoc); + + return SpliceTy; +} + +ExprResult Sema::BuildReflectionSpliceExpr(SourceLocation LSplice, + Expr *Operand, + SourceLocation RSplice, + bool AllowMemberReference) { + if (isa(Operand) && + !Operand->isTypeDependent() && !Operand->isValueDependent()) { + SmallVector Diags; + Expr::EvalResult ER; + ER.Diag = &Diags; + + if (!Operand->EvaluateAsRValue(ER, Context, true)) { + Diag(Operand->getExprLoc(), diag::err_splice_operand_not_constexpr); + for (PartialDiagnosticAt PD : Diags) + Diag(PD.first, PD.second); + return ExprError(); + } + + if (!ER.Val.isReflection()) { + Diag(Operand->getExprLoc(), diag::err_splice_operand_not_reflection); + return ExprError(); + } + ReflectionValue RV = ER.Val.getReflection(); + + switch (RV.getKind()) { + case ReflectionValue::RK_declaration: { + Decl *TheDecl = RV.getAsDecl(); + + // Class members may not be implicitly referenced through a splice. + if (!AllowMemberReference && + (isa(TheDecl) || + (isa(TheDecl) && + dyn_cast(TheDecl)->isInstance()))) { + Diag(Operand->getExprLoc(), + diag::err_dependent_splice_implicit_member_reference) + << Operand->getSourceRange(); + Diag(Operand->getExprLoc(), + diag::note_dependent_splice_explicit_this_may_fix); + return ExprError(); + } + + // Create a new DeclRefExpr, since the operand of the reflect expression + // was parsed in an unevaluated context (but a splice expression is not + // necessarily, and frequently not, in such a context). + Operand = CreateRefToDecl(*this, cast(TheDecl), + Operand->getExprLoc()); + MarkDeclRefReferenced(cast(Operand), nullptr); + Operand = CXXExprSpliceExpr::Create(Context, Operand->getValueKind(), + LSplice, Operand, RSplice, + AllowMemberReference); + break; + } + case ReflectionValue::RK_const_value: { + Operand = RV.getAsConstValueExpr(); + if (!isConstantEvaluatedContext() && !isa(Operand)) { + Operand = ConstantExpr::Create(Context, Operand, + ConstantResultStorageKind::APValue, + true); + ExprEvalContexts.back().ImmediateInvocationCandidates.emplace_back( + cast(Operand), 0); + } + Operand = CXXExprSpliceExpr::Create(Context, VK_PRValue, LSplice, Operand, + RSplice, AllowMemberReference); + break; + } + case ReflectionValue::RK_template: { + // TODO(P2996): Implement splicing of class and function templates used in + // a call expression. + Diag(Operand->getExprLoc(), diag::err_unsupported_splice_kind) + << "templates" << 1 << 1; + return ExprError(); + } + case ReflectionValue::RK_type: + case ReflectionValue::RK_namespace: + case ReflectionValue::RK_base_specifier: + case ReflectionValue::RK_data_member_spec: + Diag(Operand->getExprLoc(), diag::err_unexpected_reflection_kind) << 1; + return ExprError(); + } + return Operand; + } + return CXXExprSpliceExpr::Create(Context, Operand->getValueKind(), LSplice, + Operand, RSplice, AllowMemberReference); +} + +DeclResult Sema::BuildReflectionSpliceNamespace(SourceLocation LSplice, + Expr *Operand, + SourceLocation RSplice) { + if (Operand->isTypeDependent() || Operand->isValueDependent()) { + auto *Splice = cast(Operand); + return DependentNamespaceDecl::Create(Context, CurContext, Splice); + } + + SmallVector Diags; + Expr::EvalResult ER; + ER.Diag = &Diags; + + if (!Operand->EvaluateAsRValue(ER, Context, true)) { + Diag(Operand->getExprLoc(), diag::err_splice_operand_not_constexpr); + for (PartialDiagnosticAt PD : Diags) + Diag(PD.first, PD.second); + return DeclError(); + } + + if (!ER.Val.isReflection()) { + Diag(Operand->getExprLoc(), diag::err_splice_operand_not_reflection); + return DeclError(); + } + ReflectionValue &R = ER.Val.getReflection(); + + if (R.getKind() != ReflectionValue::RK_namespace) { + Diag(Operand->getExprLoc(), diag::err_unexpected_reflection_kind) << 2; + return DeclError(); + } else if (isa(R.getAsNamespace())) { + Diag(Operand->getExprLoc(), + diag::err_splice_global_scope_as_namespace); + return DeclError(); + } + + return R.getAsNamespace(); +} + +DeclContext *Sema::TryFindDeclContextOf(const Expr *E) { + if (E->isTypeDependent() || E->isValueDependent()) + return nullptr; + + SmallVector Diags; + Expr::EvalResult ER; + ER.Diag = &Diags; + + Expr::EvalResult Result; + if (!E->EvaluateAsRValue(Result, Context, true)) { + Diag(E->getExprLoc(), diag::err_splice_operand_not_constexpr); + for (PartialDiagnosticAt PD : Diags) + Diag(PD.first, PD.second); + return nullptr; + } + + ReflectionValue Reflection = Result.Val.getReflection(); + switch (Reflection.getKind()) { + case ReflectionValue::RK_type: { + QualType QT = Reflection.getAsType(); + if (const TagType *TT = QT->getAs()) + return TT->getDecl(); + + Diag(E->getExprLoc(), diag::err_expected_class_or_namespace) + << QT << getLangOpts().CPlusPlus; + return nullptr; + } + case ReflectionValue::RK_namespace: { + Decl *NS = Reflection.getAsNamespace(); + if (auto *A = dyn_cast(NS)) + NS = A->getNamespace(); + return cast(NS); + } + case ReflectionValue::RK_const_value: + case ReflectionValue::RK_declaration: + case ReflectionValue::RK_template: + case ReflectionValue::RK_base_specifier: + case ReflectionValue::RK_data_member_spec: + Diag(E->getExprLoc(), diag::err_expected_class_or_namespace) + << "spliced entity" << getLangOpts().CPlusPlus; + return nullptr; + } + llvm_unreachable("unknown reflection kind"); +} diff --git a/clang/lib/Sema/SemaTemplate.cpp b/clang/lib/Sema/SemaTemplate.cpp index 73030cf4b7220..b1d034e603eb4 100644 --- a/clang/lib/Sema/SemaTemplate.cpp +++ b/clang/lib/Sema/SemaTemplate.cpp @@ -1,5 +1,7 @@ //===------- SemaTemplate.cpp - Semantic Analysis for C++ Templates -------===// // +// Copyright 2024 Bloomberg Finance L.P. +// // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception @@ -219,6 +221,8 @@ TemplateNameKind Sema::isTemplateName(Scope *S, // Let the parser know whether we found nothing or found functions; if we // found nothing, we want to more carefully check whether this is actually // a function template name versus some other kind of undeclared identifier. + if (isReflectionContext()) + return TNK_Non_template; return AssumedTemplate == AssumedTemplateKind::FoundNothing ? TNK_Undeclared_template : TNK_Function_template; @@ -939,6 +943,9 @@ static TemplateArgumentLoc translateTemplateArgument(Sema &SemaRef, case ParsedTemplateArgument::NonType: { Expr *E = static_cast(Arg.getAsExpr()); + if (auto *S = dyn_cast(E)) + return TemplateArgumentLoc(TemplateArgument(S), S); + return TemplateArgumentLoc(TemplateArgument(E), E); } @@ -954,6 +961,11 @@ static TemplateArgumentLoc translateTemplateArgument(Sema &SemaRef, Arg.getScopeSpec().getWithLocInContext(SemaRef.Context), Arg.getLocation(), Arg.getEllipsisLoc()); } + + case ParsedTemplateArgument::IndeterminateSplice: { + CXXIndeterminateSpliceExpr *Splice = Arg.getAsIndeterminateSplice(); + return TemplateArgumentLoc(TemplateArgument(Splice), Splice); + } } llvm_unreachable("Unhandled parsed template argument"); @@ -1458,6 +1470,8 @@ QualType Sema::CheckNonTypeTemplateParameterType(QualType T, T->isMemberPointerType() || // -- std::nullptr_t, or T->isNullPtrType() || + // -- std::meta::info, or + T->isReflectionType() || // -- a type that contains a placeholder type. T->isUndeducedType()) { // C++ [temp.param]p5: The top-level cv-qualifiers on the template-parameter @@ -4921,6 +4935,8 @@ static bool isTemplateArgumentTemplateParameter( case TemplateArgument::Null: case TemplateArgument::NullPtr: case TemplateArgument::Integral: + case TemplateArgument::Reflection: + case TemplateArgument::IndeterminateSplice: case TemplateArgument::Declaration: case TemplateArgument::StructuralValue: case TemplateArgument::Pack: @@ -5796,7 +5812,21 @@ bool Sema::CheckTemplateTypeArgument( diagnoseMissingTemplateArguments(Name, SR.getEnd()); return true; } + case TemplateArgument::IndeterminateSplice: { + // These are dependent and will be converted during substitution. + SugaredConverted.push_back(Arg); + CanonicalConverted.push_back(Arg); + return false; + } case TemplateArgument::Expression: { + // Check if this is an expansion of a pack of IndeterminateSplices. + if (auto *P = dyn_cast(Arg.getAsExpr()); + P && isa(P->getPattern())) { + SugaredConverted.push_back(Arg); + CanonicalConverted.push_back(Arg); + return false; + } + // We have a template type parameter but the template argument is an // expression; see if maybe it is missing the "typename" keyword. CXXScopeSpec SS; @@ -6255,6 +6285,8 @@ bool Sema::CheckTemplateArgument( case TemplateArgument::Declaration: case TemplateArgument::Integral: + case TemplateArgument::Reflection: + case TemplateArgument::IndeterminateSplice: case TemplateArgument::StructuralValue: case TemplateArgument::NullPtr: // We've already checked this template argument, so just copy @@ -6401,7 +6433,23 @@ bool Sema::CheckTemplateArgument( Context.getCanonicalTemplateArgument(Arg.getArgument())); break; + case TemplateArgument::IndeterminateSplice: + // These are dependent and cannot yet be validated. Assume valid for now. + SugaredConverted.push_back(Arg.getArgument()); + CanonicalConverted.push_back(Arg.getArgument()); + break; + case TemplateArgument::Expression: + if (auto *E = Arg.getArgument().getAsExpr(); + isa(E) && + isa( + dyn_cast(E)->getPattern())) { + SugaredConverted.push_back(Arg.getArgument()); + CanonicalConverted.push_back(Arg.getArgument()); + break; + } + [[fallthrough]]; + case TemplateArgument::Type: // We have a template template parameter but the template // argument does not refer to a template. @@ -6411,6 +6459,7 @@ bool Sema::CheckTemplateArgument( case TemplateArgument::Declaration: case TemplateArgument::Integral: + case TemplateArgument::Reflection: case TemplateArgument::StructuralValue: case TemplateArgument::NullPtr: llvm_unreachable("non-type argument with template template parameter"); @@ -6934,6 +6983,11 @@ bool UnnamedLocalNoLinkageFinder::VisitDecltypeType(const DecltypeType*) { return false; } +bool UnnamedLocalNoLinkageFinder::VisitReflectionSpliceType( + const ReflectionSpliceType* T) { + return Visit(T->getUnderlyingType()); +} + bool UnnamedLocalNoLinkageFinder::VisitPackIndexingType( const PackIndexingType *) { return false; @@ -7068,6 +7122,7 @@ bool UnnamedLocalNoLinkageFinder::VisitNestedNameSpecifier( case NestedNameSpecifier::NamespaceAlias: case NestedNameSpecifier::Global: case NestedNameSpecifier::Super: + case NestedNameSpecifier::IndeterminateSplice: return false; case NestedNameSpecifier::TypeSpec: @@ -7812,7 +7867,11 @@ ExprResult Sema::CheckTemplateArgument(NonTypeTemplateParmDecl *Param, auto *PE = dyn_cast(Arg); if (PE) Arg = PE->getPattern(); - ExprResult E = ImpCastExprToType( + ExprResult E; + if (isa(Arg)) + E = Arg; + else + E = ImpCastExprToType( Arg, ParamType.getNonLValueExprType(Context), CK_Dependent, ParamType->isLValueReferenceType() ? VK_LValue : ParamType->isRValueReferenceType() ? VK_XValue @@ -7831,6 +7890,12 @@ ExprResult Sema::CheckTemplateArgument(NonTypeTemplateParmDecl *Param, return E; } + if (isa(Arg)) { + SugaredConverted = TemplateArgument(Arg, Arg); + CanonicalConverted = SugaredConverted; + return Arg; + } + QualType CanonParamType = Context.getCanonicalType(ParamType); // Avoid making a copy when initializing a template parameter of class type // from a template parameter object of the same type. This is going beyond @@ -7963,6 +8028,14 @@ ExprResult Sema::CheckTemplateArgument(NonTypeTemplateParmDecl *Param, if (Value.isAddrLabelDiff()) return Diag(StartLoc, diag::err_non_type_template_arg_addr_label_diff); + if (ParamType->isReflectionType()) { + SugaredConverted = TemplateArgument(Context, Value.getReflection()); + CanonicalConverted = TemplateArgument(Context, Value.getReflection()); + + return ArgResult.get(); + } + + SugaredConverted = TemplateArgument(Context, ParamType, Value); CanonicalConverted = TemplateArgument(Context, CanonParamType, Value); return ArgResult.get(); @@ -8594,6 +8667,42 @@ static Expr *BuildExpressionFromIntegralTemplateArgumentValue( return E; } +static ExprResult +BuildExpressionFromReflection(Sema &S, const ReflectionValue &R, + SourceLocation Loc) { + switch (R.getKind()) { + case ReflectionValue::RK_type: + return CXXReflectExpr::Create(S.Context, Loc, Loc, R.getAsType()); + case ReflectionValue::RK_const_value: + return CXXReflectExpr::Create(S.Context, Loc, R.getAsConstValueExpr()); + case ReflectionValue::RK_declaration: + return CXXReflectExpr::Create(S.Context, Loc, Loc, R.getAsDecl()); + case ReflectionValue::RK_template: + return CXXReflectExpr::Create(S.Context, Loc, Loc, R.getAsTemplate()); + case ReflectionValue::RK_namespace: + return CXXReflectExpr::Create(S.Context, Loc, Loc, R.getAsNamespace()); + case ReflectionValue::RK_base_specifier: + return CXXReflectExpr::Create(S.Context, Loc, Loc, R.getAsBaseSpecifier()); + case ReflectionValue::RK_data_member_spec: + return CXXReflectExpr::Create(S.Context, Loc, Loc, R.getAsDataMemberSpec()); + } + llvm_unreachable("unknown reflection kind"); +} + +/// Construct a new expression that refers to the given reflection template +/// argument with the given source-location information. +/// +/// This routine takes care of the mapping from an integral template argument +/// (which may have any integral type) to the appropriate literal value. +ExprResult +Sema::BuildExpressionFromReflectionTemplateArgument(const TemplateArgument &Arg, + SourceLocation Loc) { + assert(Arg.getKind() == TemplateArgument::Reflection && + "Operation is only valid for reflection template arguments"); + + return BuildExpressionFromReflection(*this, Arg.getAsReflection(), Loc); +} + static Expr *BuildExpressionFromNonTypeTemplateArgumentValue( Sema &S, QualType T, const APValue &Val, SourceLocation Loc) { auto MakeInitList = [&](ArrayRef Elts) -> Expr * { @@ -8655,7 +8764,7 @@ static Expr *BuildExpressionFromNonTypeTemplateArgumentValue( case APValue::Indeterminate: llvm_unreachable("Unexpected APValue kind."); case APValue::LValue: - case APValue::MemberPointer: + case APValue::MemberPointer: { // There isn't necessarily a valid equivalent source-level syntax for // these; in particular, a naive lowering might violate access control. // So for now we lower to a ConstantExpr holding the value, wrapped around @@ -8669,6 +8778,10 @@ static Expr *BuildExpressionFromNonTypeTemplateArgumentValue( auto *OVE = new (S.Context) OpaqueValueExpr(Loc, T, VK); return ConstantExpr::Create(S.Context, OVE, Val); } + case APValue::Reflection: { + return BuildExpressionFromReflection(S, Val.getReflection(), Loc).get(); + } + } llvm_unreachable("Unhandled APValue::ValueKind enum"); } @@ -8695,6 +8808,12 @@ Sema::BuildExpressionFromNonTypeTemplateArgument(const TemplateArgument &Arg, return BuildExpressionFromIntegralTemplateArgumentValue( *this, Arg.getIntegralType(), Arg.getAsIntegral(), Loc); + case TemplateArgument::Reflection: + return BuildExpressionFromReflectionTemplateArgument(Arg, Loc); + + case TemplateArgument::IndeterminateSplice: + return Arg.getAsIndeterminateSplice(); + case TemplateArgument::StructuralValue: return BuildExpressionFromNonTypeTemplateArgumentValue( *this, Arg.getStructuralValueType(), Arg.getAsStructuralValue(), Loc); diff --git a/clang/lib/Sema/SemaTemplateDeduction.cpp b/clang/lib/Sema/SemaTemplateDeduction.cpp index 0b6375001f532..497b133dcddbc 100644 --- a/clang/lib/Sema/SemaTemplateDeduction.cpp +++ b/clang/lib/Sema/SemaTemplateDeduction.cpp @@ -1,5 +1,7 @@ //===- SemaTemplateDeduction.cpp - Template Argument Deduction ------------===// // +// Copyright 2024 Bloomberg Finance L.P. +// // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception @@ -268,6 +270,31 @@ checkDeducedTemplateArguments(ASTContext &Context, // All other combinations are incompatible. return DeducedTemplateArgument(); + case TemplateArgument::Reflection: + // If we deduced a constant in one case and either a dependent expression or + // declaration in another case, keep the integral constant. + // If both are integral constants with the same value, keep that value. + if (Y.getKind() == TemplateArgument::Expression || + Y.getKind() == TemplateArgument::Declaration || + (Y.getKind() == TemplateArgument::Reflection && + X.getAsReflection() == Y.getAsReflection())) + return X; + + // All other combinations are incompatible. + return DeducedTemplateArgument(); + + case TemplateArgument::IndeterminateSplice: + if (Y.getKind() == TemplateArgument::Type || + Y.getKind() == TemplateArgument::Expression || + Y.getKind() == TemplateArgument::Declaration || + Y.getKind() == TemplateArgument::Reflection || + Y.getKind() == TemplateArgument::IndeterminateSplice || + Y.getKind() == TemplateArgument::Template) + return X; + + // All other combinations are incompatible. + return DeducedTemplateArgument(); + case TemplateArgument::StructuralValue: // If we deduced a value and a dependent expression, keep the value. if (Y.getKind() == TemplateArgument::Expression || @@ -319,8 +346,7 @@ checkDeducedTemplateArguments(ASTContext &Context, return X; // If we deduced a declaration and an integral constant, keep the - // integral constant and whichever type did not come from an array - // bound. + // integral constant and whichever type did not come from an array bound. if (Y.getKind() == TemplateArgument::Integral) { if (Y.wasDeducedFromArrayBound()) return TemplateArgument(Context, Y.getAsIntegral(), @@ -328,6 +354,12 @@ checkDeducedTemplateArguments(ASTContext &Context, return Y; } + // If we deduced a declaration and a reflection constant, keep the + // reflection constant. + if (Y.getKind() == TemplateArgument::Reflection) { + return Y; + } + // If we deduced two declarations, make sure that they refer to the // same declaration. if (Y.getKind() == TemplateArgument::Declaration && @@ -2272,6 +2304,7 @@ static TemplateDeductionResult DeduceTemplateArgumentsByTypeMatch( case Type::DependentName: case Type::UnresolvedUsing: case Type::Decltype: + case Type::ReflectionSplice: case Type::UnaryTransform: case Type::DeducedTemplateSpecialization: case Type::DependentTemplateSpecialization: @@ -2355,6 +2388,18 @@ DeduceTemplateArguments(Sema &S, TemplateParameterList *TemplateParams, Info.SecondArg = A; return TemplateDeductionResult::NonDeducedMismatch; + case TemplateArgument::Reflection: + if (A.getKind() == TemplateArgument::Reflection && + P.getAsReflection() == A.getAsReflection()) + return TemplateDeductionResult::Success; + + Info.FirstArg = P; + Info.SecondArg = A; + return TemplateDeductionResult::NonDeducedMismatch; + + case TemplateArgument::IndeterminateSplice: + llvm_unreachable("TODO"); + case TemplateArgument::StructuralValue: if (A.getKind() == TemplateArgument::StructuralValue && A.structurallyEquals(P)) @@ -2371,6 +2416,8 @@ DeduceTemplateArguments(Sema &S, TemplateParameterList *TemplateParams, case TemplateArgument::Integral: case TemplateArgument::Expression: case TemplateArgument::StructuralValue: + case TemplateArgument::Reflection: + case TemplateArgument::IndeterminateSplice: return DeduceNonTypeTemplateArgument( S, TemplateParams, NTTP, DeducedTemplateArgument(A), A.getNonTypeTemplateArgumentType(), Info, Deduced); @@ -2579,6 +2626,12 @@ static bool isSameTemplateArg(ASTContext &Context, case TemplateArgument::Integral: return hasSameExtendedValue(X.getAsIntegral(), Y.getAsIntegral()); + case TemplateArgument::Reflection: + return X.getAsReflection() == Y.getAsReflection(); + + case TemplateArgument::IndeterminateSplice: + return false; + case TemplateArgument::StructuralValue: return X.structurallyEquals(Y); @@ -2668,6 +2721,15 @@ Sema::getTrivialTemplateArgumentLoc(const TemplateArgument &Arg, return TemplateArgumentLoc(TemplateArgument(E), E); } + case TemplateArgument::Reflection: { + Expr *E = + BuildExpressionFromReflectionTemplateArgument(Arg, Loc).getAs(); + return TemplateArgumentLoc(TemplateArgument(E), E); + } + + case TemplateArgument::IndeterminateSplice: + llvm_unreachable("TODO: unimplemented"); + case TemplateArgument::Template: case TemplateArgument::TemplateExpansion: { NestedNameSpecifierLocBuilder Builder; @@ -3863,8 +3925,9 @@ static QualType GetTypeOfFunction(Sema &S, const OverloadExpr::FindResult &R, if (CXXMethodDecl *Method = dyn_cast(Fn)) if (Method->isImplicitObjectMemberFunction()) { // An instance method that's referenced in a form that doesn't - // look like a member pointer is just invalid. - if (!R.HasFormOfMemberPointer) + // look like a member pointer is just invalid (unless in the context of + // taking its reflection). + if (!R.HasFormOfMemberPointer && !S.isReflectionContext()) return {}; return S.Context.getMemberPointerType(Fn->getType(), @@ -5202,6 +5265,11 @@ TypeSourceInfo *Sema::ReplaceAutoTypeSourceInfo(TypeSourceInfo *TypeWithAuto, } void Sema::DiagnoseAutoDeductionFailure(VarDecl *VDecl, Expr *Init) { + // If we're deducing to infer the type of the operand of a reflect expression, + // elide the diagnostic to allow a more relevant error further up the stack. + if (isReflectionContext()) + return; + if (isa(Init)) Diag(VDecl->getLocation(), VDecl->isInitCapture() @@ -6510,6 +6578,13 @@ MarkUsedTemplateParameters(ASTContext &Ctx, QualType T, OnlyDeduced, Depth, Used); break; + case Type::ReflectionSplice: + if (!OnlyDeduced) + MarkUsedTemplateParameters(Ctx, + cast(T)->getOperand(), + OnlyDeduced, Depth, Used); + break; + case Type::PackIndexing: if (!OnlyDeduced) { MarkUsedTemplateParameters(Ctx, cast(T)->getPattern(), @@ -6576,6 +6651,8 @@ MarkUsedTemplateParameters(ASTContext &Ctx, switch (TemplateArg.getKind()) { case TemplateArgument::Null: case TemplateArgument::Integral: + case TemplateArgument::Reflection: + case TemplateArgument::IndeterminateSplice: case TemplateArgument::Declaration: case TemplateArgument::NullPtr: case TemplateArgument::StructuralValue: diff --git a/clang/lib/Sema/SemaTemplateInstantiate.cpp b/clang/lib/Sema/SemaTemplateInstantiate.cpp index d7b7291091ecb..3a77016659e29 100644 --- a/clang/lib/Sema/SemaTemplateInstantiate.cpp +++ b/clang/lib/Sema/SemaTemplateInstantiate.cpp @@ -1,5 +1,7 @@ //===------- SemaTemplateInstantiate.cpp - C++ Template Instantiation ------===/ // +// Copyright 2024 Bloomberg Finance L.P. +// // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception @@ -1554,9 +1556,10 @@ namespace { const CodeAlignAttr *TransformCodeAlignAttr(const CodeAlignAttr *CA); ExprResult TransformPredefinedExpr(PredefinedExpr *E); ExprResult TransformDeclRefExpr(DeclRefExpr *E); + ExprResult TransformCXXReflectExpr(CXXReflectExpr *E); ExprResult TransformCXXDefaultArgExpr(CXXDefaultArgExpr *E); - ExprResult TransformTemplateParmRefExpr(DeclRefExpr *E, + ExprResult TransformTemplateParmRefExpr(Expr *E, NonTypeTemplateParmDecl *D); ExprResult TransformSubstNonTypeTemplateParmPackExpr( SubstNonTypeTemplateParmPackExpr *E); @@ -1567,7 +1570,7 @@ namespace { ExprResult RebuildVarDeclRefExpr(VarDecl *PD, SourceLocation Loc); /// Transform a reference to a function or init-capture parameter pack. - ExprResult TransformFunctionParmPackRefExpr(DeclRefExpr *E, VarDecl *PD); + ExprResult TransformFunctionParmPackRefExpr(Expr *E, VarDecl *PD); /// Transform a FunctionParmPackExpr which was built when we couldn't /// expand a function parameter pack reference which refers to an expanded @@ -2025,6 +2028,11 @@ TemplateName TemplateInstantiator::TransformTemplateName( Arg = getPackSubstitutedTemplateArgument(getSema(), Arg); } + if (Arg.getKind() == TemplateArgument::IndeterminateSplice) { + CXXIndeterminateSpliceExpr *Splice = Arg.getAsIndeterminateSplice(); + return SemaRef.Context.getDependentTemplateName(Splice); + } + TemplateName Template = Arg.getAsTemplate().getNameToSubstitute(); assert(!Template.isNull() && "Null template template argument"); assert(!Template.getAsQualifiedTemplateName() && @@ -2066,7 +2074,7 @@ TemplateInstantiator::TransformPredefinedExpr(PredefinedExpr *E) { } ExprResult -TemplateInstantiator::TransformTemplateParmRefExpr(DeclRefExpr *E, +TemplateInstantiator::TransformTemplateParmRefExpr(Expr *E, NonTypeTemplateParmDecl *NTTP) { // If the corresponding template argument is NULL or non-existent, it's // because we are performing instantiation from explicitly-specified @@ -2093,6 +2101,10 @@ TemplateInstantiator::TransformTemplateParmRefExpr(DeclRefExpr *E, return Arg.getAsExpr(); } + SourceLocation Loc = E->getBeginLoc(); + if (auto *DRE = dyn_cast(E)) + Loc = DRE->getLocation(); + auto [AssociatedDecl, _] = TemplateArgs.getAssociatedDecl(NTTP->getDepth()); std::optional PackIndex; if (NTTP->isParameterPack()) { @@ -2104,7 +2116,7 @@ TemplateInstantiator::TransformTemplateParmRefExpr(DeclRefExpr *E, // out of it yet. Therefore, we'll build an expression to hold on to that // argument pack. QualType TargetType = SemaRef.SubstType(NTTP->getType(), TemplateArgs, - E->getLocation(), + Loc, NTTP->getDeclName()); if (TargetType.isNull()) return ExprError(); @@ -2115,13 +2127,13 @@ TemplateInstantiator::TransformTemplateParmRefExpr(DeclRefExpr *E, // FIXME: Pass in Final. return new (SemaRef.Context) SubstNonTypeTemplateParmPackExpr( ExprType, TargetType->isReferenceType() ? VK_LValue : VK_PRValue, - E->getLocation(), Arg, AssociatedDecl, NTTP->getPosition()); + Loc, Arg, AssociatedDecl, NTTP->getPosition()); } PackIndex = getPackIndex(Arg); Arg = getPackSubstitutedTemplateArgument(getSema(), Arg); } // FIXME: Don't put subst node on Final replacement. - return transformNonTypeTemplateParmRef(AssociatedDecl, NTTP, E->getLocation(), + return transformNonTypeTemplateParmRef(AssociatedDecl, NTTP, Loc, Arg, PackIndex); } @@ -2215,6 +2227,8 @@ ExprResult TemplateInstantiator::transformNonTypeTemplateParmRef( refParam = true; } } + } else if (arg.getKind() == TemplateArgument::IndeterminateSplice) { + return arg.getAsIndeterminateSplice(); } else if (arg.getKind() == TemplateArgument::Declaration || arg.getKind() == TemplateArgument::NullPtr) { if (arg.getKind() == TemplateArgument::Declaration) { @@ -2233,6 +2247,11 @@ ExprResult TemplateInstantiator::transformNonTypeTemplateParmRef( assert(!paramType->isDependentType() && "param type still dependent"); result = SemaRef.BuildExpressionFromDeclTemplateArgument(arg, paramType, loc); refParam = paramType->isReferenceType(); + } else if (arg.getKind() == TemplateArgument::Reflection) { + result = SemaRef.BuildExpressionFromReflectionTemplateArgument(arg, loc); + assert(result.isInvalid() || + SemaRef.Context.hasSameType(result.get()->getType(), + SemaRef.Context.MetaInfoTy)); } else { QualType paramType = arg.getNonTypeTemplateArgumentType(); result = SemaRef.BuildExpressionFromNonTypeTemplateArgument(arg, loc); @@ -2352,7 +2371,7 @@ TemplateInstantiator::TransformFunctionParmPackExpr(FunctionParmPackExpr *E) { } ExprResult -TemplateInstantiator::TransformFunctionParmPackRefExpr(DeclRefExpr *E, +TemplateInstantiator::TransformFunctionParmPackRefExpr(Expr *E, VarDecl *PD) { typedef LocalInstantiationScope::DeclArgumentPack DeclArgumentPack; llvm::PointerUnion *Found @@ -2404,6 +2423,29 @@ TemplateInstantiator::TransformDeclRefExpr(DeclRefExpr *E) { return inherited::TransformDeclRefExpr(E); } +ExprResult +TemplateInstantiator::TransformCXXReflectExpr(CXXReflectExpr *E) { + if (E->getOperand().getKind() == ReflectionValue::RK_declaration) { + Decl *D = E->getOperand().getAsDecl(); + + // Handle references to non-type template parameters and non-type template + // parameter packs. + if (NonTypeTemplateParmDecl *NTTP = dyn_cast(D); + NTTP && NTTP->getDepth() < TemplateArgs.getNumLevels()) { + ExprResult Result = TransformTemplateParmRefExpr(E, NTTP); + return getSema().BuildCXXReflectExpr(E->getOperatorLoc(), Result.get()); + } + + // Handle references to function parameter packs. + if (VarDecl *PD = dyn_cast(D); PD && PD->isParameterPack()) { + ExprResult Result = TransformFunctionParmPackRefExpr(E, PD); + return getSema().BuildCXXReflectExpr(E->getOperatorLoc(), Result.get()); + } + } + + return inherited::TransformCXXReflectExpr(E); +} + ExprResult TemplateInstantiator::TransformCXXDefaultArgExpr( CXXDefaultArgExpr *E) { assert(!cast(E->getParam()->getDeclContext())-> @@ -2506,6 +2548,16 @@ TemplateInstantiator::TransformTemplateTypeParmType(TypeLocBuilder &TLB, return NewT; } + if (Arg.getKind() == TemplateArgument::IndeterminateSplice) { + CXXIndeterminateSpliceExpr *Splice = Arg.getAsIndeterminateSplice(); + QualType UnderlyingTy = SemaRef.Context.DependentTy; + + QualType Ty = SemaRef.Context.getReflectionSpliceType(Splice, + UnderlyingTy); + TLB.push(Ty); + return Ty; + } + auto [AssociatedDecl, Final] = TemplateArgs.getAssociatedDecl(T->getDepth()); std::optional PackIndex; @@ -3373,7 +3425,9 @@ Sema::SubstBaseSpecifiers(CXXRecordDecl *Instantiation, if (RD->isInvalidDecl()) Instantiation->setInvalidDecl(); } - InstantiatedBases.push_back(new (Context) CXXBaseSpecifier(Base)); + CXXBaseSpecifier *Specifier = new (Context) CXXBaseSpecifier(Base); + Specifier->setDerived(Instantiation); + InstantiatedBases.push_back(Specifier); continue; } diff --git a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp index 1cb071e4eb7d1..f3b4384b1b9cc 100644 --- a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp +++ b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp @@ -992,8 +992,61 @@ TemplateDeclInstantiator::VisitNamespaceDecl(NamespaceDecl *D) { llvm_unreachable("Namespaces cannot be instantiated"); } +Decl * +TemplateDeclInstantiator::VisitDependentNamespaceDecl( + DependentNamespaceDecl *D) { + ExprResult ER = SemaRef.SubstExpr(D->getSpliceExpr(), TemplateArgs); + if (ER.isInvalid()) + return nullptr; + auto *Splice = cast(ER.get()); + assert(!Splice->isValueDependent()); + + DeclResult DR = + SemaRef.ActOnCXXSpliceExpectingNamespace(Splice->getLSpliceLoc(), + Splice->getOperand(), + Splice->getRSpliceLoc()); + if (DR.isInvalid()) + return nullptr; + return DR.get(); +} + Decl * TemplateDeclInstantiator::VisitNamespaceAliasDecl(NamespaceAliasDecl *D) { + NamedDecl *NSDecl = D->getAliasedNamespace(); + + if (D->isDependent()) { + NestedNameSpecifierLoc QualifierLoc = D->getQualifierLoc(); + if (NestedNameSpecifier *NNS = QualifierLoc.getNestedNameSpecifier(); + NNS && NNS->isDependent()) { + QualifierLoc = SemaRef.SubstNestedNameSpecifierLoc(QualifierLoc, + TemplateArgs); + + CXXScopeSpec SS; + SS.Adopt(QualifierLoc); + return SemaRef.ActOnNamespaceAliasDef(/*Scope=*/nullptr, + D->getNamespaceLoc(), + D->getAliasLoc(), + D->getIdentifier(), + SS, D->getBeginLoc(), + D->getNamespace()->getIdentifier()); + } else if (auto *DNSD = dyn_cast(NSDecl)) { + assert(!D->getQualifierLoc()); + + Decl *Transformed = Visit(DNSD); + if (!Transformed) + return nullptr; + NSDecl = cast(Transformed); + } else if (auto *SubAlias = dyn_cast(NSDecl)) { + assert(SubAlias->isDependent()); + Decl *SubAliasResult = Visit(SubAlias); + if (!SubAliasResult) + return nullptr; + NSDecl = cast(SubAliasResult); + } else { + llvm_unreachable("unknown dependent namespace alias kind"); + } + } + NamespaceAliasDecl *Inst = NamespaceAliasDecl::Create(SemaRef.Context, Owner, D->getNamespaceLoc(), @@ -1001,7 +1054,7 @@ TemplateDeclInstantiator::VisitNamespaceAliasDecl(NamespaceAliasDecl *D) { D->getIdentifier(), D->getQualifierLoc(), D->getTargetNameLoc(), - D->getNamespace()); + NSDecl); Owner->addDecl(Inst); return Inst; } diff --git a/clang/lib/Sema/SemaTemplateVariadic.cpp b/clang/lib/Sema/SemaTemplateVariadic.cpp index 903fbfd18e779..d715e85912ec0 100644 --- a/clang/lib/Sema/SemaTemplateVariadic.cpp +++ b/clang/lib/Sema/SemaTemplateVariadic.cpp @@ -1,5 +1,7 @@ //===------- SemaTemplateVariadic.cpp - C++ Variadic Templates ------------===/ // +// Copyright 2024 Bloomberg Finance L.P. +// // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception @@ -98,6 +100,22 @@ namespace { return true; } + // Record occurrences of function and non-type template parameters packs in + // an expression. + bool VisitCXXReflectExpr(CXXReflectExpr *E) { + if (E->getOperand().getKind() == ReflectionValue::RK_declaration) { + ValueDecl *VD = E->getOperand().getAsDecl(); + if (VD->isParameterPack()) + addUnexpanded(VD, E->getExprLoc()); + } else if (E->getOperand().getKind() == ReflectionValue::RK_template) { + TemplateName TName = E->getOperand().getAsTemplate(); + if (TName.containsUnexpandedParameterPack()) { + addUnexpanded(TName.getAsTemplateDecl()); + } + } + return true; + } + /// Record occurrences of template template parameter packs. bool TraverseTemplateName(TemplateName Template) { if (auto *TTP = dyn_cast_or_null( @@ -600,7 +618,18 @@ Sema::ActOnPackExpansion(const ParsedTemplateArgument &Arg, } return Arg.getTemplatePackExpansion(EllipsisLoc); + + case ParsedTemplateArgument::IndeterminateSplice: { + ExprResult Result = ActOnPackExpansion(Arg.getAsIndeterminateSplice(), + EllipsisLoc); + if (Result.isInvalid()) + return ParsedTemplateArgument(); + + return ParsedTemplateArgument(ParsedTemplateArgument::NonType, Result.get(), + Arg.getLocation()); } + } + llvm_unreachable("Unhandled template argument kind?"); } @@ -890,6 +919,7 @@ bool Sema::containsUnexpandedParameterPacks(Declarator &D) { case TST_typeof_unqualExpr: case TST_typeofExpr: case TST_decltype: + case TST_type_splice: case TST_bitint: if (DS.getRepAsExpr() && DS.getRepAsExpr()->containsUnexpandedParameterPack()) @@ -1074,6 +1104,11 @@ static bool isParameterPack(Expr *PackExpression) { if (auto *D = dyn_cast(PackExpression); D) { ValueDecl *VD = D->getDecl(); return VD->isParameterPack(); + } else if (auto *D = dyn_cast(PackExpression); + D && D->getOperand().getKind() == + ReflectionValue::RK_declaration) { + ValueDecl *VD = D->getOperand().getAsDecl(); + return VD->isParameterPack(); } return false; } @@ -1166,6 +1201,10 @@ TemplateArgumentLoc Sema::getTemplateArgumentPackExpansionPattern( Expr *Pattern = Expansion->getPattern(); Ellipsis = Expansion->getEllipsisLoc(); NumExpansions = Expansion->getNumExpansions(); + + if (auto *S = dyn_cast(Pattern)) + return TemplateArgumentLoc(S, S); + return TemplateArgumentLoc(Pattern, Pattern); } @@ -1180,10 +1219,14 @@ TemplateArgumentLoc Sema::getTemplateArgumentPackExpansionPattern( case TemplateArgument::NullPtr: case TemplateArgument::Template: case TemplateArgument::Integral: + case TemplateArgument::Reflection: case TemplateArgument::StructuralValue: case TemplateArgument::Pack: case TemplateArgument::Null: return TemplateArgumentLoc(); + + case TemplateArgument::IndeterminateSplice: + llvm_unreachable("pack of splices should be Expression type"); } llvm_unreachable("Invalid TemplateArgument Kind!"); @@ -1231,6 +1274,7 @@ std::optional Sema::getFullyPackExpandedSize(TemplateArgument Arg) { case TemplateArgument::NullPtr: case TemplateArgument::TemplateExpansion: case TemplateArgument::Integral: + case TemplateArgument::Reflection: case TemplateArgument::StructuralValue: case TemplateArgument::Pack: case TemplateArgument::Null: diff --git a/clang/lib/Sema/SemaType.cpp b/clang/lib/Sema/SemaType.cpp index 8762744396f4d..25bc5f49f1f0e 100644 --- a/clang/lib/Sema/SemaType.cpp +++ b/clang/lib/Sema/SemaType.cpp @@ -1,5 +1,7 @@ //===--- SemaType.cpp - Semantic Analysis for Types -----------------------===// // +// Copyright 2024 Bloomberg Finance L.P. +// // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception @@ -1674,6 +1676,21 @@ static QualType ConvertDeclSpecToType(TypeProcessingState &state) { } break; } + + case DeclSpec::TST_type_splice: { + CXXIndeterminateSpliceExpr *E = + dyn_cast(DS.getRepAsExpr()); + assert(E && "Didn't get an expression for type-splice?"); + // TypeQuals handled by caller. + Result = S.BuildReflectionSpliceType(E->getLSpliceLoc(), E->getOperand(), + E->getRSpliceLoc(), /*Complain=*/true); + if (Result.isNull()) { + Result = Context.IntTy; + declarator.setInvalidType(true); + } + break; + } + case DeclSpec::TST_typename_pack_indexing: { Expr *E = DS.getPackIndexingExpr(); assert(E && "Didn't get an expression for pack indexing"); @@ -3730,6 +3747,7 @@ static QualType GetDeclSpecTypeForDeclarator(TypeProcessingState &state, Error = 9; // Block literal break; case DeclaratorContext::TemplateArg: + case DeclaratorContext::ReflectOperator: // Within a template argument list, a deduced template specialization // type will be reinterpreted as a template template argument. if (isa(Deduced) && @@ -3875,6 +3893,7 @@ static QualType GetDeclSpecTypeForDeclarator(TypeProcessingState &state, case DeclaratorContext::TemplateArg: case DeclaratorContext::TemplateTypeArg: case DeclaratorContext::Association: + case DeclaratorContext::ReflectOperator: DiagID = diag::err_type_defined_in_type_specifier; break; case DeclaratorContext::Prototype: @@ -4982,6 +5001,7 @@ static TypeSourceInfo *GetFullTypeForDeclarator(TypeProcessingState &state, case DeclaratorContext::FunctionalCast: case DeclaratorContext::RequiresExpr: case DeclaratorContext::Association: + case DeclaratorContext::ReflectOperator: // Don't infer in these contexts. break; } @@ -5783,6 +5803,20 @@ static TypeSourceInfo *GetFullTypeForDeclarator(TypeProcessingState &state, ClsType = Context.getElaboratedType(ElaboratedTypeKeyword::None, NNSPrefix, ClsType); break; + case NestedNameSpecifier::IndeterminateSplice: { + CXXIndeterminateSpliceExpr *E = + const_cast(NNS->getAsSpliceExpr()); + TypeResult TR = S.ActOnCXXSpliceExpectingType(E->getLSpliceLoc(), E, + E->getRSpliceLoc(), + /*Complain=*/true); + if (TR.isInvalid()) + D.setInvalidType(true); + else { + ClsType = TR.get().get(); + if (auto *LIT = dyn_cast(ClsType)) + ClsType = LIT->getType(); + } break; + } } } else { S.Diag(DeclType.Mem.Scope().getBeginLoc(), @@ -6095,6 +6129,7 @@ static TypeSourceInfo *GetFullTypeForDeclarator(TypeProcessingState &state, case DeclaratorContext::TemplateArg: case DeclaratorContext::TemplateTypeArg: case DeclaratorContext::Association: + case DeclaratorContext::ReflectOperator: // FIXME: We may want to allow parameter packs in block-literal contexts // in the future. S.Diag(D.getEllipsisLoc(), @@ -6315,7 +6350,8 @@ namespace { } void VisitTemplateSpecializationTypeLoc(TemplateSpecializationTypeLoc TL) { TypeSourceInfo *TInfo = nullptr; - Sema::GetTypeFromParser(DS.getRepAsType(), &TInfo); + if (DS.getTypeSpecType() != TST_type_splice) + Sema::GetTypeFromParser(DS.getRepAsType(), &TInfo); // If we got no declarator info from previous Sema routines, // just fill with the typespec loc. diff --git a/clang/lib/Sema/TreeTransform.h b/clang/lib/Sema/TreeTransform.h index ab97b375f5161..1baec62e783db 100644 --- a/clang/lib/Sema/TreeTransform.h +++ b/clang/lib/Sema/TreeTransform.h @@ -1,5 +1,7 @@ //===------- TreeTransform.h - Semantic Tree Transformation -----*- C++ -*-===// // +// Copyright 2024 Bloomberg Finance L.P. +// // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception @@ -42,6 +44,7 @@ #include "clang/Sema/SemaOpenACC.h" #include "llvm/ADT/ArrayRef.h" #include "llvm/Support/ErrorHandling.h" +#include "llvm/Support/SaveAndRestore.h" #include #include @@ -1055,6 +1058,12 @@ class TreeTransform { /// Subclasses may override this routine to provide different behavior. QualType RebuildDecltypeType(Expr *Underlying, SourceLocation Loc); + /// Build a new type loc from a C++2c reflection splice (P2996). + QualType RebuildReflectionSpliceTypeLoc(TypeLocBuilder &TLB, + SourceLocation LSpliceLoc, + Expr *E, + SourceLocation RSpliceLoc); + QualType RebuildPackIndexingType(QualType Pattern, Expr *IndexExpr, SourceLocation Loc, SourceLocation EllipsisLoc, @@ -3929,6 +3938,8 @@ class TreeTransform { case TemplateArgument::Null: case TemplateArgument::Integral: + case TemplateArgument::Reflection: + case TemplateArgument::IndeterminateSplice: case TemplateArgument::Declaration: case TemplateArgument::StructuralValue: case TemplateArgument::Pack: @@ -4115,8 +4126,10 @@ ExprResult TreeTransform::TransformInitializer(Expr *Init, if (!Init) return Init; - if (auto *FE = dyn_cast(Init)) - Init = FE->getSubExpr(); + if (auto *FE = dyn_cast(Init)) { + if (FE->getSubExpr()) + Init = FE->getSubExpr(); + } if (auto *AIL = dyn_cast(Init)) { OpaqueValueExpr *OVE = AIL->getCommonExpr(); @@ -4428,6 +4441,75 @@ NestedNameSpecifierLoc TreeTransform::TransformNestedNameSpecifierLoc( } return NestedNameSpecifierLoc(); } + case NestedNameSpecifier::IndeterminateSplice: { + CXXIndeterminateSpliceExpr *Splice = + const_cast(QNNS->getAsSpliceExpr()); + + // Transform the splice operand (resolve template parameters, etc). + ExprResult ER; + { + EnterExpressionEvaluationContext Context( + getSema(), Sema::ExpressionEvaluationContext::ConstantEvaluated); + ER = getDerived().TransformExpr(Splice); + } + + if (ER.isInvalid()) + return NestedNameSpecifierLoc(); + else if (ER.get()->isValueDependent()) { + SS.MakeIndeterminateSplice(SemaRef.Context, + cast(ER.get()), + Q.getLocalEndLoc()); + break; + } + Expr *Operand = ER.get(); + + // Should have a non-dependent expression now: Evaluate it. + Expr::EvalResult Result; + if (!ER.get()->EvaluateAsRValue(Result, SemaRef.Context)) + return NestedNameSpecifierLoc(); + ReflectionValue Reflection = Result.Val.getReflection(); + + // Form new nested-name-specifier component based on the reflection kind. + if (Reflection.getKind() == ReflectionValue::RK_type) { + // Verify that the resulting type is a tag type. + if (!isa(Reflection.getAsType())) { + SemaRef.Diag(Splice->getExprLoc(), diag::err_nested_name_spec_non_tag) + << Reflection.getAsType() << SS.getRange(); + return NestedNameSpecifierLoc(); + } + + // Add a component with a ReflectionSpliceType holding the transformed + // non-dependent Expr. + TypeLocBuilder TLB; + QualType QT = SemaRef.BuildReflectionSpliceType( + Splice->getLSpliceLoc(), Operand, Splice->getRSpliceLoc(), + /*Complain=*/true); + ReflectionSpliceTypeLoc RSTL = TLB.push(QT); + RSTL.setLSpliceLoc(Splice->getLSpliceLoc()); + RSTL.setRSpliceLoc(Splice->getRSpliceLoc()); + + SS.Extend(SemaRef.Context, Splice->getLSpliceLoc(), + TLB.getTypeLocInContext(getSema().Context, QT), + Q.getLocalEndLoc()); + } else if (Reflection.getKind() == ReflectionValue::RK_namespace) { + Decl *D = Reflection.getAsNamespace(); + if (auto *TD = dyn_cast(D)) + SS.MakeGlobal(SemaRef.Context, Splice->getLSpliceLoc()); + else if (auto *ND = dyn_cast(D)) + SS.Extend(SemaRef.Context, ND, Splice->getLSpliceLoc(), + Q.getLocalEndLoc()); + else if (auto *NAD = dyn_cast(D)) + SS.Extend(SemaRef.Context, NAD, Splice->getLSpliceLoc(), + Q.getLocalEndLoc()); + else + llvm_unreachable("unknown reflection namespace decl kind"); + } else { + SemaRef.Diag(Splice->getExprLoc(), + diag::err_expected_class_or_namespace) + << "spliced entity" << SemaRef.getLangOpts().CPlusPlus; + } + break; + } } // The qualifier-in-scope and object type only apply to the leftmost entity. @@ -4615,6 +4697,7 @@ bool TreeTransform::TransformTemplateArgument( llvm_unreachable("Unexpected TemplateArgument"); case TemplateArgument::Integral: + case TemplateArgument::Reflection: case TemplateArgument::NullPtr: case TemplateArgument::Declaration: case TemplateArgument::StructuralValue: { @@ -4641,6 +4724,10 @@ bool TreeTransform::TransformTemplateArgument( Output = TemplateArgumentLoc( TemplateArgument(getSema().Context, Arg.getAsIntegral(), NewT), TemplateArgumentLocInfo()); + else if (Arg.getKind() == TemplateArgument::Reflection) + Output = TemplateArgumentLoc( + TemplateArgument(getSema().Context, Arg.getAsReflection()), + TemplateArgumentLocInfo()); else if (Arg.getKind() == TemplateArgument::NullPtr) Output = TemplateArgumentLoc(TemplateArgument(NewT, /*IsNullPtr=*/true), TemplateArgumentLocInfo()); @@ -4657,6 +4744,36 @@ bool TreeTransform::TransformTemplateArgument( return false; } + case TemplateArgument::IndeterminateSplice: { + // Template argument expressions are constant expressions. + EnterExpressionEvaluationContext Unevaluated( + getSema(), + Uneval ? Sema::ExpressionEvaluationContext::Unevaluated + : Sema::ExpressionEvaluationContext::ConstantEvaluated, + Sema::ReuseLambdaContextDecl, /*ExprContext=*/ + Sema::ExpressionEvaluationContextRecord::EK_TemplateArgument); + + ExprResult ER = getDerived().TransformCXXIndeterminateSpliceExpr( + Input.getArgument().getAsIndeterminateSplice()); + if (ER.isInvalid()) + return true; + CXXIndeterminateSpliceExpr *NewSplice = + cast(ER.get()); + + ParsedTemplateArgument ParsedTAs[1] = { + SemaRef.ActOnTemplateIndeterminateSpliceArgument(NewSplice) + }; + if (ParsedTAs[0].isInvalid()) + return true; + + TemplateArgumentListInfo TAListInfo; + SemaRef.translateTemplateArguments(ParsedTAs, TAListInfo); + assert(TAListInfo.size() == 1); + + Output = TAListInfo[0]; + return false; + } + case TemplateArgument::Type: { TypeSourceInfo *DI = Input.getTypeSourceInfo(); if (!DI) @@ -6643,6 +6760,25 @@ TreeTransform::TransformPackIndexingType(TypeLocBuilder &TLB, return Out; } +template +QualType TreeTransform::TransformReflectionSpliceType( + TypeLocBuilder &TLB, + ReflectionSpliceTypeLoc TL) { + const ReflectionSpliceType *T = TL.getTypePtr(); + + ExprResult ER; + { + EnterExpressionEvaluationContext Context( + getSema(), Sema::ExpressionEvaluationContext::ConstantEvaluated); + ER = getDerived().TransformExpr(T->getOperand()); + } + if (ER.isInvalid()) + return QualType(); + + return getDerived().RebuildReflectionSpliceTypeLoc( + TLB, ER.get()->getBeginLoc(), ER.get(), ER.get()->getEndLoc()); +} + template QualType TreeTransform::TransformUnaryTransformType( TypeLocBuilder &TLB, @@ -8466,7 +8602,7 @@ TreeTransform::TransformDependentCoawaitExpr(DependentCoawaitExpr *E) { cast(LookupResult.get())); } -template +template ExprResult TreeTransform::TransformCoyieldExpr(CoyieldExpr *E) { ExprResult Result = getDerived().TransformInitializer(E->getOperand(), @@ -8479,6 +8615,146 @@ TreeTransform::TransformCoyieldExpr(CoyieldExpr *E) { return getDerived().RebuildCoyieldExpr(E->getKeywordLoc(), Result.get()); } +template +ExprResult +TreeTransform::TransformCXXReflectExpr(CXXReflectExpr *E) { + const ReflectionValue &Refl = E->getOperand(); + + switch (Refl.getKind()) { + case ReflectionValue::RK_type: { + QualType Old = Refl.getAsType(); + + // Adjust the type in case we get parsed type information. + if (const LocInfoType *LIT = dyn_cast(Old)) { + Old = LIT->getType(); + } + + QualType New = getDerived().TransformType(Old); + if (New.isNull()) { + return ExprError(); + } + return getSema().BuildCXXReflectExpr(E->getOperatorLoc(), + E->getArgLoc(), New); + } + case ReflectionValue::RK_const_value: { + ExprResult Result = getDerived().TransformExpr(Refl.getAsConstValueExpr()); + if (Result.isInvalid()) + return ExprError(); + + return getSema().BuildCXXReflectExpr(E->getOperatorLoc(), Result.get()); + } + case ReflectionValue::RK_declaration: { + Decl *Transformed = getDerived().TransformDecl(E->getExprLoc(), + Refl.getAsDecl()); + return getSema().BuildCXXReflectExpr(E->getOperatorLoc(), E->getArgLoc(), + cast(Transformed)); + } + case ReflectionValue::RK_template: { + CXXScopeSpec SS; + if (QualifiedTemplateName *QTN = + Refl.getAsTemplate().getAsQualifiedTemplateName()) { + SS.MakeTrivial(getSema().Context, QTN->getQualifier(), SourceRange()); + } + + TemplateName Template = getDerived().TransformTemplateName( + SS, Refl.getAsTemplate(), E->getArgLoc()); + if (Template.isNull()) + return true; + + return getSema().BuildCXXReflectExpr(E->getOperatorLoc(), E->getArgLoc(), + Template); + } + case ReflectionValue::RK_namespace: { + Decl *Transformed = + getDerived().TransformDecl(E->getExprLoc(), Refl.getAsNamespace()); + return getSema().BuildCXXReflectExpr(E->getOperatorLoc(), E->getArgLoc(), + Transformed); + } + case ReflectionValue::RK_base_specifier: + case ReflectionValue::RK_data_member_spec: + return E; + } + llvm_unreachable("invalid reflection"); +} + +template +ExprResult +TreeTransform::TransformCXXMetafunctionExpr(CXXMetafunctionExpr *E) { + SmallVector Args(E->getNumArgs()); + for (unsigned I = 0; I < E->getNumArgs(); ++I) { + ExprResult Arg = getDerived().TransformExpr(E->getArg(I)); + if (Arg.isInvalid()) + return ExprError(); + Args[I] = Arg.get(); + } + + return getSema().BuildCXXMetafunctionExpr(E->getKwLoc(), + E->getLParenLoc(), + E->getRParenLoc(), + E->getMetaFnID(), E->getImpl(), + Args); +} + +template +ExprResult +TreeTransform::TransformCXXIndeterminateSpliceExpr( + CXXIndeterminateSpliceExpr *E) { + // Splice expressions are evaluated immediately, so this is similar to + // rebuilding an immediate invocation. + llvm::SaveAndRestore DisableIITracking( + getSema().RebuildingImmediateInvocation, true); + + ExprResult Result = getDerived().TransformExpr(E->getOperand()); + if (Result.isInvalid()) + return ExprError(); + + return getSema().BuildCXXIndeterminateSpliceExpr(E->getLSpliceLoc(), + Result.get(), + E->getRSpliceLoc()); +} + +template +ExprResult +TreeTransform::TransformCXXExprSpliceExpr(CXXExprSpliceExpr *E) { + ExprResult ER; + { + EnterExpressionEvaluationContext Context( + getSema(), Sema::ExpressionEvaluationContext::ConstantEvaluated); + ER = getDerived().TransformExpr(E->getOperand()); + } + if (ER.isInvalid()) + return ExprError(); + + return getSema().BuildReflectionSpliceExpr(E->getLSpliceLoc(), ER.get(), + E->getRSpliceLoc(), + E->allowMemberReference()); +} + +template +ExprResult +TreeTransform::TransformCXXDependentMemberSpliceExpr( + CXXDependentMemberSpliceExpr *E) { + ExprResult Base = getDerived().TransformExpr(E->getBase()); + ExprResult RHS = getDerived().TransformExpr(E->getRHS()); + + return getSema().BuildMemberReferenceExpr( + nullptr, Base.get(), E->getOpLoc(), + E->isArrow()? tok::arrow : tok::period, + cast(RHS.get()), SourceLocation()); +} + +template +ExprResult +TreeTransform::TransformStackLocationExpr(StackLocationExpr *E) { + return E; +} + +template +ExprResult +TreeTransform::TransformValueOfLValueExpr(ValueOfLValueExpr *E) { + return E; +} + // Objective-C Statements. template @@ -11113,7 +11389,9 @@ StmtResult TreeTransform::TransformOpenACCComputeConstruct( template ExprResult TreeTransform::TransformConstantExpr(ConstantExpr *E) { - return TransformExpr(E->getSubExpr()); + if (auto *SE = E->getSubExpr()) + return TransformExpr(SE); + return E; } template @@ -15596,6 +15874,14 @@ QualType TreeTransform::RebuildDecltypeType(Expr *E, SourceLocation) { return SemaRef.BuildDecltypeType(E); } +template +QualType TreeTransform::RebuildReflectionSpliceTypeLoc( + TypeLocBuilder &TLB, SourceLocation LSpliceLoc, Expr *E, + SourceLocation RSpliceLoc) { + return SemaRef.BuildReflectionSpliceTypeLoc(TLB, LSpliceLoc, E, RSpliceLoc, + /*Complain=*/true); +} + template QualType TreeTransform::RebuildPackIndexingType( QualType Pattern, Expr *IndexExpr, SourceLocation Loc, diff --git a/clang/lib/Serialization/ASTCommon.cpp b/clang/lib/Serialization/ASTCommon.cpp index 6110e287b7fb5..f999d6a459d4c 100644 --- a/clang/lib/Serialization/ASTCommon.cpp +++ b/clang/lib/Serialization/ASTCommon.cpp @@ -183,6 +183,9 @@ serialization::TypeIdxFromBuiltin(const BuiltinType *BT) { case BuiltinType::Char32: ID = PREDEF_TYPE_CHAR32_ID; break; + case BuiltinType::MetaInfo: + ID = PREDEF_TYPE_META_INFO_ID; + break; case BuiltinType::Overload: ID = PREDEF_TYPE_OVERLOAD_ID; break; @@ -358,6 +361,7 @@ bool serialization::isRedeclarableDeclKind(unsigned Kind) { case Decl::Namespace: case Decl::NamespaceAlias: + case Decl::DependentNamespace: case Decl::Typedef: case Decl::TypeAlias: case Decl::Enum: diff --git a/clang/lib/Serialization/ASTReader.cpp b/clang/lib/Serialization/ASTReader.cpp index fa5bb9f2d5435..c51ac4ecebe39 100644 --- a/clang/lib/Serialization/ASTReader.cpp +++ b/clang/lib/Serialization/ASTReader.cpp @@ -6953,6 +6953,11 @@ void TypeLocReader::VisitDecltypeTypeLoc(DecltypeTypeLoc TL) { TL.setRParenLoc(readSourceLocation()); } +void TypeLocReader::VisitReflectionSpliceTypeLoc(ReflectionSpliceTypeLoc TL) { + TL.setLSpliceLoc(readSourceLocation()); + TL.setRSpliceLoc(readSourceLocation()); +} + void TypeLocReader::VisitPackIndexingTypeLoc(PackIndexingTypeLoc TL) { TL.setEllipsisLoc(readSourceLocation()); } @@ -7327,6 +7332,9 @@ QualType ASTReader::GetType(TypeID ID) { case PREDEF_TYPE_CHAR32_ID: T = Context.Char32Ty; break; + case PREDEF_TYPE_META_INFO_ID: + T = Context.MetaInfoTy; + break; case PREDEF_TYPE_OBJC_ID: T = Context.ObjCBuiltinIdTy; break; @@ -7473,6 +7481,7 @@ ASTRecordReader::readTemplateArgumentLocInfo(TemplateArgument::ArgKind Kind) { } case TemplateArgument::Null: case TemplateArgument::Integral: + case TemplateArgument::Reflection: case TemplateArgument::Declaration: case TemplateArgument::NullPtr: case TemplateArgument::StructuralValue: @@ -9185,14 +9194,13 @@ void ASTRecordReader::readUnresolvedSet(LazyASTUnresolvedSet &Set) { CXXBaseSpecifier ASTRecordReader::readCXXBaseSpecifier() { bool isVirtual = readBool(); - bool isBaseOfClass = readBool(); AccessSpecifier AS = static_cast(readInt()); bool inheritConstructors = readBool(); TypeSourceInfo *TInfo = readTypeSourceInfo(); + CXXRecordDecl *Derived = readDeclAs(); SourceRange Range = readSourceRange(); SourceLocation EllipsisLoc = readSourceLocation(); - CXXBaseSpecifier Result(Range, isVirtual, isBaseOfClass, AS, TInfo, - EllipsisLoc); + CXXBaseSpecifier Result(Range, isVirtual, AS, TInfo, Derived, EllipsisLoc); Result.setInheritConstructors(inheritConstructors); return Result; } @@ -9318,6 +9326,14 @@ ASTRecordReader::readNestedNameSpecifierLoc() { Builder.MakeSuper(Context, RD, Range.getBegin(), Range.getEnd()); break; } + + case NestedNameSpecifier::IndeterminateSplice: { + CXXIndeterminateSpliceExpr *Expr = + reinterpret_cast(readExpr()); + SourceLocation ColonColonLoc = readSourceLocation(); + Builder.MakeIndeterminateSplice(Context, Expr, ColonColonLoc); + break; + } } } diff --git a/clang/lib/Serialization/ASTReaderStmt.cpp b/clang/lib/Serialization/ASTReaderStmt.cpp index f0984c3e46960..62cc402d5776f 100644 --- a/clang/lib/Serialization/ASTReaderStmt.cpp +++ b/clang/lib/Serialization/ASTReaderStmt.cpp @@ -495,6 +495,36 @@ void ASTStmtReader::VisitCoyieldExpr(CoyieldExpr *E) { E->OpaqueValue = cast_or_null(Record.readSubStmt()); } +void ASTStmtReader::VisitCXXReflectExpr(CXXReflectExpr *E) { + llvm_unreachable("unimplemented"); +} + +void ASTStmtReader::VisitCXXMetafunctionExpr(CXXMetafunctionExpr *E) { + llvm_unreachable("unimplemented"); +} + +void ASTStmtReader::VisitCXXIndeterminateSpliceExpr( + CXXIndeterminateSpliceExpr *E) { + llvm_unreachable("unimplemented"); +} + +void ASTStmtReader::VisitCXXExprSpliceExpr(CXXExprSpliceExpr *E) { + llvm_unreachable("unimplemented"); +} + +void ASTStmtReader::VisitCXXDependentMemberSpliceExpr( + CXXDependentMemberSpliceExpr *E) { + llvm_unreachable("unimplemented"); +} + +void ASTStmtReader::VisitStackLocationExpr(StackLocationExpr *E) { + llvm_unreachable("unimplemented"); +} + +void ASTStmtReader::VisitValueOfLValueExpr(ValueOfLValueExpr *E) { + llvm_unreachable("unimplemented"); +} + void ASTStmtReader::VisitDependentCoawaitExpr(DependentCoawaitExpr *E) { VisitExpr(E); E->KeywordLoc = readSourceLocation(); diff --git a/clang/lib/Serialization/ASTWriter.cpp b/clang/lib/Serialization/ASTWriter.cpp index baf03f69d7306..dac3dfde9a2f0 100644 --- a/clang/lib/Serialization/ASTWriter.cpp +++ b/clang/lib/Serialization/ASTWriter.cpp @@ -469,6 +469,11 @@ void TypeLocWriter::VisitDecltypeTypeLoc(DecltypeTypeLoc TL) { addSourceLocation(TL.getRParenLoc()); } +void TypeLocWriter::VisitReflectionSpliceTypeLoc(ReflectionSpliceTypeLoc TL) { + addSourceLocation(TL.getLSpliceLoc()); + addSourceLocation(TL.getRSpliceLoc()); +} + void TypeLocWriter::VisitUnaryTransformTypeLoc(UnaryTransformTypeLoc TL) { addSourceLocation(TL.getKWLoc()); addSourceLocation(TL.getLParenLoc()); @@ -982,6 +987,7 @@ void ASTWriter::WriteBlockInfoBlock() { RECORD(TYPE_DECAYED); RECORD(TYPE_ADJUSTED); RECORD(TYPE_OBJC_TYPE_PARAM); + RECORD(TYPE_REFLECTION_SPLICE); RECORD(LOCAL_REDECLARATIONS); RECORD(DECL_TYPEDEF); RECORD(DECL_TYPEALIAS); @@ -5679,6 +5685,7 @@ void ASTRecordWriter::AddTemplateArgumentLocInfo( break; case TemplateArgument::Null: case TemplateArgument::Integral: + case TemplateArgument::Reflection: case TemplateArgument::Declaration: case TemplateArgument::NullPtr: case TemplateArgument::StructuralValue: @@ -5954,6 +5961,10 @@ void ASTRecordWriter::AddNestedNameSpecifierLoc(NestedNameSpecifierLoc NNS) { AddDeclRef(NNS.getNestedNameSpecifier()->getAsRecordDecl()); AddSourceRange(NNS.getLocalSourceRange()); break; + + case NestedNameSpecifier::IndeterminateSplice: + AddSourceRange(NNS.getLocalSourceRange()); + break; } } } @@ -6008,10 +6019,10 @@ void ASTRecordWriter::AddUnresolvedSet(const ASTUnresolvedSet &Set) { // FIXME: Move this out of the main ASTRecordWriter interface. void ASTRecordWriter::AddCXXBaseSpecifier(const CXXBaseSpecifier &Base) { Record->push_back(Base.isVirtual()); - Record->push_back(Base.isBaseOfClass()); Record->push_back(Base.getAccessSpecifierAsWritten()); Record->push_back(Base.getInheritConstructors()); AddTypeSourceInfo(Base.getTypeSourceInfo()); + AddDeclRef(Base.getDerived()); AddSourceRange(Base.getSourceRange()); AddSourceLocation(Base.isPackExpansion()? Base.getEllipsisLoc() : SourceLocation()); diff --git a/clang/lib/Serialization/ASTWriterStmt.cpp b/clang/lib/Serialization/ASTWriterStmt.cpp index 0651614e2ce54..f032cd801978e 100644 --- a/clang/lib/Serialization/ASTWriterStmt.cpp +++ b/clang/lib/Serialization/ASTWriterStmt.cpp @@ -458,6 +458,52 @@ void ASTStmtWriter::VisitCoyieldExpr(CoyieldExpr *E) { Code = serialization::EXPR_COYIELD; } +void ASTStmtWriter::VisitCXXReflectExpr(CXXReflectExpr *E) { + VisitExpr(E); + Record.AddSourceLocation(E->getOperatorLoc()); + Record.AddReflectionValue(E->getOperand()); + Code = serialization::EXPR_REFLECT; +} + +void ASTStmtWriter::VisitCXXMetafunctionExpr(CXXMetafunctionExpr *E) { + VisitExpr(E); + Record.AddSourceLocation(E->getKwLoc()); + // TODO(P2996): Note that this cannot be safely deserialized with the current + // model. + Code = serialization::EXPR_METAFUNCTION; +} + +void ASTStmtWriter::VisitCXXIndeterminateSpliceExpr( + CXXIndeterminateSpliceExpr *E) { + VisitExpr(E); + Code = serialization::EXPR_SPLICE; +} + +void ASTStmtWriter::VisitCXXExprSpliceExpr(CXXExprSpliceExpr *E) { + VisitExpr(E); + // TODO(P2996): Implement this. + Code = serialization::EXPR_EXPR_SPLICE; +} + +void ASTStmtWriter::VisitCXXDependentMemberSpliceExpr( + CXXDependentMemberSpliceExpr *E) { + VisitExpr(E); + // TODO(P2996): Implement this. + Code = serialization::EXPR_DEPENDENT_MEMBER_SPLICE; +} + +void ASTStmtWriter::VisitStackLocationExpr(StackLocationExpr *E) { + VisitExpr(E); + // TODO(P2996): Implement this. + Code = serialization::EXPR_STACK_LOCATION; +} + +void ASTStmtWriter::VisitValueOfLValueExpr(ValueOfLValueExpr *E) { + VisitExpr(E); + // TODO(P2996): Implement this. + Code = serialization::EXPR_VALUE_OF_LVALUE; +} + void ASTStmtWriter::VisitDependentCoawaitExpr(DependentCoawaitExpr *E) { VisitExpr(E); Record.AddSourceLocation(E->getKeywordLoc()); diff --git a/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp b/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp index 09c69f9612d96..dafba7fccce6b 100644 --- a/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp +++ b/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp @@ -1721,6 +1721,13 @@ void ExprEngine::Visit(const Stmt *S, ExplodedNode *Pred, switch (S->getStmtClass()) { // C++, OpenMP and ARC stuff we don't support yet. case Stmt::CXXDependentScopeMemberExprClass: + case Stmt::CXXReflectExprClass: + case Stmt::CXXMetafunctionExprClass: + case Stmt::CXXIndeterminateSpliceExprClass: + case Stmt::CXXExprSpliceExprClass: + case Stmt::CXXDependentMemberSpliceExprClass: + case Stmt::StackLocationExprClass: + case Stmt::ValueOfLValueExprClass: case Stmt::CXXTryStmtClass: case Stmt::CXXTypeidExprClass: case Stmt::CXXUuidofExprClass: diff --git a/clang/test/CXX/dcl.dcl/dcl.attr/dcl.attr.grammar/p2-1z.cpp b/clang/test/CXX/dcl.dcl/dcl.attr/dcl.attr.grammar/p2-1z.cpp index 192fa12610987..b010299239174 100644 --- a/clang/test/CXX/dcl.dcl/dcl.attr/dcl.attr.grammar/p2-1z.cpp +++ b/clang/test/CXX/dcl.dcl/dcl.attr/dcl.attr.grammar/p2-1z.cpp @@ -8,8 +8,8 @@ [[using foo ] // expected-error {{expected ':'}} ] extern int n; -[[using 42:]] extern int n; // expected-error {{expected identifier}} -[[using clang:]] extern int n; // ok +[[using 42:]] extern int n; // expected-error {{expected identifier}} expected-warning {{enable reflection}} +[[using clang:]] extern int n; // expected-warning {{enable reflection}} [[using blah: clang::optnone]] extern int n; // expected-error {{attribute with scope specifier cannot follow}} expected-warning {{only applies to functions}} [[using clang: unknown_attr]] extern int n; // expected-warning {{unknown attribute}} diff --git a/clang/test/Parser/cxx-casting.cpp b/clang/test/Parser/cxx-casting.cpp index 34c3a1a2460b5..dc208a6e3a619 100644 --- a/clang/test/Parser/cxx-casting.cpp +++ b/clang/test/Parser/cxx-casting.cpp @@ -126,7 +126,8 @@ void test3() { // Make sure that parser doesn't expand '[:' to '< ::' ::D[:F> A5; // expected-error {{class template '::D' requires template arguments}} \ // expected-error {{expected expression}} \ - // expected-error {{expected unqualified-id}} + // expected-error {{expected unqualified-id}} \ + // expected-warning {{enable reflection features}} } // Ensure that a C-style cast doesn't turn off colon protection. diff --git a/clang/test/ParserOpenACC/parse-cache-construct.c b/clang/test/ParserOpenACC/parse-cache-construct.c index fd161c03c09f7..0cee6cde0d628 100644 --- a/clang/test/ParserOpenACC/parse-cache-construct.c +++ b/clang/test/ParserOpenACC/parse-cache-construct.c @@ -132,8 +132,9 @@ void func() { } for (int i = 0; i < 10; ++i) { - // expected-error@+2{{expected expression}} - // expected-warning@+1{{OpenACC construct 'cache' not yet implemented, pragma ignored}} + // expected-error@+3{{expected expression}} + // expected-warning@+2{{OpenACC construct 'cache' not yet implemented, pragma ignored}} + // expected-warning@+1{{enable reflection features}} #pragma acc cache(readonly:ArrayPtr[5:]) } diff --git a/clang/test/ParserOpenACC/parse-cache-construct.cpp b/clang/test/ParserOpenACC/parse-cache-construct.cpp index f0a35824696d8..b4123060a1b01 100644 --- a/clang/test/ParserOpenACC/parse-cache-construct.cpp +++ b/clang/test/ParserOpenACC/parse-cache-construct.cpp @@ -84,14 +84,16 @@ void use() { #pragma acc cache(Arrs.MemArr[3:4].array[4]) } for (int i = 0; i < 10; ++i) { - // expected-error@+3{{expected ']'}} - // expected-note@+2{{to match this '['}} - // expected-warning@+1{{OpenACC construct 'cache' not yet implemented, pragma ignored}} + // expected-error@+4{{expected ']'}} + // expected-note@+3{{to match this '['}} + // expected-warning@+2{{OpenACC construct 'cache' not yet implemented, pragma ignored}} + // expected-warning@+1{{enable reflection features}} #pragma acc cache(Arrs.MemArr[3:4:].array[4]) } for (int i = 0; i < 10; ++i) { - // expected-error@+2{{expected expression}} - // expected-warning@+1{{OpenACC construct 'cache' not yet implemented, pragma ignored}} + // expected-error@+3{{expected expression}} + // expected-warning@+2{{OpenACC construct 'cache' not yet implemented, pragma ignored}} + // expected-warning@+1 2{{enable reflection features}} #pragma acc cache(Arrs.MemArr[:].array[4]) } for (int i = 0; i < 10; ++i) { @@ -100,15 +102,17 @@ void use() { #pragma acc cache(Arrs.MemArr[::].array[4]) } for (int i = 0; i < 10; ++i) { - // expected-error@+4{{expected expression}} - // expected-error@+3{{expected ']'}} - // expected-note@+2{{to match this '['}} - // expected-warning@+1{{OpenACC construct 'cache' not yet implemented, pragma ignored}} + // expected-error@+5{{expected expression}} + // expected-error@+4{{expected ']'}} + // expected-note@+3{{to match this '['}} + // expected-warning@+2{{OpenACC construct 'cache' not yet implemented, pragma ignored}} + // expected-warning@+1 2{{enable reflection features}} #pragma acc cache(Arrs.MemArr[: :].array[4]) } for (int i = 0; i < 10; ++i) { - // expected-error@+2{{expected expression}} - // expected-warning@+1{{OpenACC construct 'cache' not yet implemented, pragma ignored}} + // expected-error@+3{{expected expression}} + // expected-warning@+2{{OpenACC construct 'cache' not yet implemented, pragma ignored}} + // expected-warning@+1{{enable reflection features}} #pragma acc cache(Arrs.MemArr[3:].array[4]) } func(); diff --git a/clang/test/ParserOpenACC/parse-clauses.c b/clang/test/ParserOpenACC/parse-clauses.c index b58b332ad3245..f6005a56f66ec 100644 --- a/clang/test/ParserOpenACC/parse-clauses.c +++ b/clang/test/ParserOpenACC/parse-clauses.c @@ -507,8 +507,9 @@ void VarListClauses() { #pragma acc serial copy(HasMem.MemArr[1:3].array[1:2]), seq for(;;){} - // expected-error@+2{{expected expression}} - // expected-warning@+1{{OpenACC clause 'seq' not yet implemented, clause ignored}} + // expected-error@+3{{expected expression}} + // expected-warning@+2{{OpenACC clause 'seq' not yet implemented, clause ignored}} + // expected-warning@+1 2{{enable reflection features}} #pragma acc serial copy(HasMem.MemArr[:]), seq for(;;){} @@ -517,15 +518,17 @@ void VarListClauses() { #pragma acc serial copy(HasMem.MemArr[::]), seq for(;;){} - // expected-error@+4{{expected expression}} - // expected-error@+3{{expected ']'}} - // expected-note@+2{{to match this '['}} - // expected-warning@+1{{OpenACC clause 'seq' not yet implemented, clause ignored}} + // expected-error@+5{{expected expression}} + // expected-error@+4{{expected ']'}} + // expected-note@+3{{to match this '['}} + // expected-warning@+2{{OpenACC clause 'seq' not yet implemented, clause ignored}} + // expected-warning@+1 2{{enable reflection features}} #pragma acc serial copy(HasMem.MemArr[: :]), seq for(;;){} - // expected-error@+2{{expected expression}} - // expected-warning@+1{{OpenACC clause 'seq' not yet implemented, clause ignored}} + // expected-error@+3{{expected expression}} + // expected-warning@+2{{OpenACC clause 'seq' not yet implemented, clause ignored}} + // expected-warning@+1{{enable reflection features}} #pragma acc serial copy(HasMem.MemArr[3:]), seq for(;;){} diff --git a/clang/test/Reflection/info-equality.cpp b/clang/test/Reflection/info-equality.cpp new file mode 100644 index 0000000000000..3b00f2fb2d023 --- /dev/null +++ b/clang/test/Reflection/info-equality.cpp @@ -0,0 +1,184 @@ +//===----------------------------------------------------------------------===// +// +// Copyright 2024 Bloomberg Finance L.P. +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// RUN: %clang_cc1 %s -std=c++23 -freflection + +using info = decltype(^int); + +namespace myns { +namespace inner {} + +struct Test { + using type = int; +}; +constexpr info rTest = ^Test::type; +} + +struct Test { + using type = int; +}; + +void fn() {} +void fn2() {} + +int var = 0; +int var2 = 0; + +struct WithMembers { + int datamem; + void memfn() {} + + static int staticdatamem; + + struct inner {}; + + template + void tmemfn() {} +}; + +enum Enum { A, B, C }; +enum class EnumCls { A, B, C }; + +template void tfn() {} +template void tfn2() {} +template struct TCls {}; +template struct TCls2 {}; +template constexpr int tvar = 0; +template constexpr int tvar2 = 0; +template concept Concept = requires() { true; }; +template concept Concept2 = requires() { true; }; + +using int_alias = int; + +namespace NsAlias = ::myns; + + +// Ensure that different entities compare equally to themselves. +constexpr info refl = ^int; +static_assert(^int == ^int); +static_assert(^int == refl); +static_assert(^A == ^A); +static_assert(^EnumCls::A == ^EnumCls::A); +static_assert(^Enum == ^Enum); +static_assert(^EnumCls == ^EnumCls); +static_assert(^int_alias == ^int_alias); +static_assert(^Test::type == ^Test::type); +static_assert(^Test == ^Test); +static_assert(^Test == ^::Test); +static_assert(^myns::Test::type == ^myns::Test::type); +static_assert(^myns::Test::type == myns::rTest); +static_assert(^myns::rTest == ^myns::rTest); +static_assert(^:: == ^::); +static_assert(^myns == ^myns); +static_assert(^myns == ^::myns); +static_assert(^myns::inner == ^::myns::inner); +static_assert(^NsAlias == ^NsAlias); +static_assert(^fn == ^fn); +static_assert(^var == ^var); +static_assert(^tfn == ^tfn); +static_assert(^TCls == ^TCls); +static_assert(^tvar == ^tvar); +static_assert(^Concept == ^Concept); +static_assert(^WithMembers::datamem == ^WithMembers::datamem); +static_assert(^WithMembers::memfn == ^WithMembers::memfn); +static_assert(^WithMembers::staticdatamem == ^WithMembers::staticdatamem); +static_assert(^WithMembers::inner == ^WithMembers::inner); +static_assert(^WithMembers::tmemfn == ^WithMembers::tmemfn); +static_assert(^myns::rTest == ^myns::rTest); + +// Check equality semantics of types and type aliases. +using int_alias = int; +static_assert(^int != ^void); +static_assert(^int != ^unsigned int); +static_assert(^int != ^Enum); +static_assert(^int != ^EnumCls); +static_assert(^int_alias != ^int); +static_assert(^int_alias != ^Test::type); +static_assert(^int_alias != ^myns::Test::type); +static_assert(^Test::type != ^myns::Test::type); + +// Check equality semantics of enumerations and enumerators. +static_assert(^Enum::A != ^Enum::B); +static_assert(^Enum::A != ^EnumCls::A); +static_assert(^EnumCls::A != ^EnumCls::B); + +// Check equality semantics of some different entities. +static_assert(^fn != ^fn2); +static_assert(^fn != ^var); +static_assert(^fn != ^tvar); +static_assert(^decltype(&fn) == ^void(*)()); +static_assert(^var != ^var2); +static_assert(^tfn != ^tfn); + +// Check equality semantics of templates. +static_assert(^tfn != ^tfn2); +static_assert(^tfn != ^TCls); +static_assert(^TCls != ^TCls2); +static_assert(^tfn != ^tvar); +static_assert(^tvar != ^tvar2); +static_assert(^Concept != ^Concept2); + +// Check equality semantics of namespaces. +static_assert(^:: != ^::myns); +static_assert(^::myns != ^NsAlias); +static_assert(^::myns != ^::myns::inner); + +// Check that reflections of instantiated concepts are constant values. +static_assert(^Concept == ^true); +static_assert(^Concept == ^Concept2); + +// Check equality semantics of reflections of reflections. +static_assert(^^:: == ^^::); +static_assert(^^:: != ^::); +static_assert(^^TCls == ^^TCls); +static_assert(^^TCls != ^TCls); +static_assert(^^tfn == ^^tfn); +static_assert(^^tfn != ^tfn); +static_assert(^^int == ^^int); +static_assert(^^int != ^int); +static_assert(^^4 == ^^4); +static_assert(^^4 != ^4); + +constexpr int i = 42; +constexpr auto i_refl = ^i; +constexpr auto i_refl_copy = i_refl; +static_assert(i_refl == ^i); +static_assert(i_refl == i_refl_copy); + +constexpr int j = 42; +static_assert(^i != ^j); + +// CONFIRM: Unspecified? +// static_assert(^42 != ^42); + +consteval info local_var_reflection() { + int i; + return ^i; +} +static_assert(local_var_reflection() == local_var_reflection()); + +// Compare reflections of the same local variable in different stack frames. +namespace local_variables_different_stack_frames { +consteval bool local_var_in_diff_frames_equal(info inf, int call_depth = 0) { + int lcl = call_depth; + if (call_depth > 0) { + // These should not be equal. + // They are different variables with different values, which + // can be accessed with value_of() + return inf == ^lcl; + } + else { + return local_var_in_diff_frames_equal(^lcl, 1); + } +} +// FIXME: Should these compare equal? +// static_assert(!local_var_in_diff_frames_equal(^void)); + +} // namespace local_variables_different_stack_frames diff --git a/clang/test/Reflection/lift-operator.cpp b/clang/test/Reflection/lift-operator.cpp new file mode 100644 index 0000000000000..71e5b739ed998 --- /dev/null +++ b/clang/test/Reflection/lift-operator.cpp @@ -0,0 +1,111 @@ +//===----------------------------------------------------------------------===// +// +// Copyright 2024 Bloomberg Finance L.P. +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// RUN: %clang_cc1 %s -std=c++23 -freflection + +// Reflecting Types +using info = decltype(^void); + +constexpr info info_void = ^void; +constexpr info info_decltype = ^decltype(42); + +using alias = int; +constexpr info info_alias = ^alias; + +constexpr info info_infoint = ^decltype(^int); + +// Reflecting variables +constexpr int i = 42; +constexpr info info_i = ^i; + +// Reflecting literals +constexpr info info_42 = ^42; + +// Reflecting templates +template +void TemplateFunc(); +constexpr info info_template_func = ^TemplateFunc; + +template +int TemplateVar; +constexpr info info_template_var = ^TemplateVar; + +template +struct TemplateStruct {}; +constexpr info info_template_struct = ^TemplateStruct; + +// Reflecting members of a class +struct Members { + struct Type {}; + int member_var; + static int static_member_var; + + int member_func(); + static int static_member_func(); +}; +constexpr info info_nested_type = ^Members::Type; +constexpr info info_mem_var = ^Members::member_var; +constexpr info info_static_mem_var = ^Members::static_member_var; +constexpr info info_mem_func = ^Members::member_func; +constexpr info info_static_mem_func = ^Members::static_member_func; + +// Reflecting member templates +struct MemberTemplates { + template + struct NestedTemplateStruct {}; + + template + void template_func(); + + template + static void template_static_func(); + + template + static int template_var; +}; +constexpr info info_nested_template_struct = ^MemberTemplates::NestedTemplateStruct; +constexpr info info_nested_template_func = ^MemberTemplates::template_func; +constexpr info info_nested_template_static_func = ^MemberTemplates::template_static_func; +constexpr info info_nested_template_var = ^MemberTemplates::template_var; + +// Reflecting function scope variables +void reflect_func_scope(int param) { + constexpr info info_param = ^param; + + int local_var; + constexpr info info_local = ^local_var; + + static int static_var; + constexpr info info_static = ^static_var; + + thread_local int thread_var; + constexpr info info_thread = ^thread_var; + + struct local_type {}; + constexpr info info_type = ^local_type; +} + +// Reflecting a template parameter +template +consteval info foo() { + return ^T; +} +constexpr info info_tmplparam = foo(); + +// Reflecting values of a non-structural type +struct Nonstructural { + constexpr Nonstructural(int i) : mem(i) {} +private: + int mem; +}; +constexpr info info_nonstructural = ^Nonstructural{42}; + +namespace ns {} +constexpr info info_ns = ^ns; diff --git a/clang/test/Reflection/metafunction.cpp b/clang/test/Reflection/metafunction.cpp new file mode 100644 index 0000000000000..f5c0623f924c9 --- /dev/null +++ b/clang/test/Reflection/metafunction.cpp @@ -0,0 +1,24 @@ +//===----------------------------------------------------------------------===// +// +// Copyright 2024 Bloomberg Finance L.P. +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// RUN: %clang_cc1 %s -std=c++23 -freflection + +using info = decltype(^int); + +// Can we use a function parameter as the info parameter of __metafunction +// without generating warnings? +consteval auto metafn_info_as_func_param(info inf) +{ + class Sentinel; + + // This is unstable because it depends on metafunction#0 taking exactly + // two arguments. Easy enough to fix, though. + return __metafunction(0, inf, ^Sentinel); +} diff --git a/clang/test/Reflection/p2996-ex-forward-and-back.cpp b/clang/test/Reflection/p2996-ex-forward-and-back.cpp new file mode 100644 index 0000000000000..18f97e799b846 --- /dev/null +++ b/clang/test/Reflection/p2996-ex-forward-and-back.cpp @@ -0,0 +1,17 @@ +//===----------------------------------------------------------------------===// +// +// Copyright 2024 Bloomberg Finance L.P. +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// RUN: %clang_cc1 %s -std=c++23 -freflection + +constexpr auto r = ^int; +typename[:r:] x = 42; // Same as: int x = 42; +typename[:^char:] c = '*'; // Same as: char c = '*'; + +int main() {} diff --git a/clang/test/Reflection/p2996-ex-selecting-members-pt1.cpp b/clang/test/Reflection/p2996-ex-selecting-members-pt1.cpp new file mode 100644 index 0000000000000..07e4ba7d39028 --- /dev/null +++ b/clang/test/Reflection/p2996-ex-selecting-members-pt1.cpp @@ -0,0 +1,23 @@ +//===----------------------------------------------------------------------===// +// +// Copyright 2024 Bloomberg Finance L.P. +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// RUN: %clang_cc1 %s -std=c++23 -freflection + +struct S { unsigned i:2, j:6; }; + +consteval auto member_number(int n) { + if (n == 0) return ^S::i; + else if (n == 1) return ^S::j; +} + +int main() { + S s{0, 0}; + s.[:member_number(1):] = 42; // Same as: s.j = 42; +} diff --git a/clang/test/Reflection/reflection-of-splice.cpp b/clang/test/Reflection/reflection-of-splice.cpp new file mode 100644 index 0000000000000..6286a0d7b618c --- /dev/null +++ b/clang/test/Reflection/reflection-of-splice.cpp @@ -0,0 +1,40 @@ +//===----------------------------------------------------------------------===// +// +// Copyright 2024 Bloomberg Finance L.P. +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// RUN: %clang_cc1 %s -std=c++23 -freflection + +using info = decltype(^int); + +namespace myns { + namespace Inner {} + struct Cls {}; + template struct TCls { + void memfn(); + }; +} // namespace myns + + // =========== + // idempotency + // =========== + +namespace idempotency { +static_assert(^[:^myns::TCls:] == ^myns::TCls); +static_assert(^[:^myns:] == ^myns); +static_assert(^[:^:::] == ^::); + +static_assert(^[:^myns:]::Cls == ^myns::Cls); +static_assert(^[:^myns:]::TCls == ^myns::TCls); + +static_assert(^[:^myns::TCls:]::memfn == ^myns::TCls::memfn); +static_assert(^[:^myns::TCls:]::memfn != ^myns::TCls::memfn); + +static_assert(^[:^myns:]::TCls == ^myns::TCls); +static_assert(^[:^myns:]::Inner == ^myns::Inner); +} // namespace idempotency diff --git a/clang/test/Reflection/splice-exprs.cpp b/clang/test/Reflection/splice-exprs.cpp new file mode 100644 index 0000000000000..1de107cc0ceec --- /dev/null +++ b/clang/test/Reflection/splice-exprs.cpp @@ -0,0 +1,251 @@ +//===----------------------------------------------------------------------===// +// +// Copyright 2024 Bloomberg Finance L.P. +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// RUN: %clang_cc1 %s -std=c++23 -freflection + +using info = decltype(^int); + + // =========== + // idempotency + // =========== + +namespace idempotency { +int x; +void fn(); +struct S { + int x; + static constexpr int s_x = 11; + void fn(); + static void s_fn(); +}; +enum Enum { A, B, C }; +enum class EnumCls { A, B, C }; + +static_assert([:^4:] == 4); +static_assert(&[:^x:] == &x); +static_assert([:^fn:] == fn); + +static_assert(&[:^S::x:] == &S::x); +static_assert(&[:^S::s_x:] == &S::s_x); +static_assert(&[:^S::fn:] == &S::fn); +static_assert([:^S::s_fn:] == S::s_fn); + +static_assert([:^Enum::B:] == Enum::B); +static_assert([:^EnumCls::B:] == EnumCls::B); +} // namespace idempotency + + // ============== + // with_variables + // ============== + +namespace with_variables { +consteval int fn() { + int x = 32; + + constexpr auto rx = ^x; + ++[:rx:]; + + return x; +} +static_assert(fn() == 33); +} // namespace with_variables + + // ============== + // with_functions + // ============== + +namespace with_functions { +consteval int vanilla_fn() { return 42; } + +constexpr info r_vanilla_fn = ^vanilla_fn; +static_assert([:r_vanilla_fn:]() == 42); + +// With a dependent reflection. +template consteval int fn() { return [:R:](); } +static_assert(fn() == 42); +} // namespace with_functions + +// ============================ +// with_shadowed_function_names +// ============================ + +namespace with_shadowed_function_names { +struct B { consteval char fn() const { return 'B'; } }; +struct D : B { consteval char fn() const { return 'D'; } }; + +constexpr auto rBfn = ^B::fn; +constexpr auto rDfn = ^D::fn; + +constexpr D d; +constexpr auto rd = ^d; + +static_assert([:rd:].[:rBfn:]() == 'B'); +static_assert([:rd:].[:rDfn:]() == 'D'); + +} // namespace with_shadowed_function_names + + // ================== + // with_member_access + // ================== + +// Check use of splices in member access expressions. +namespace with_member_access { +struct S { + int j; + int k; + + consteval int getJ() const { return j; } + + template + consteval int getJPlusN() const { return j + N; } + + static consteval int eleven() { return 11; } + + template + static consteval int constant() { return N; } +}; + +// Splicing dependent member references. +template +consteval int fn() { + S s = {11, 13}; + return s.[:RMem:] + (&s)->[:RMem:]; +} +static_assert(fn<^S::j>() == 22); +static_assert(fn<^S::k>() == 26); + +// Splicing dependent member references with arrow syntax. +template +consteval int fn2() { + S s = {11, 13}; + return s.*(&[:RMem:]) + (&s)->*(&[:RMem:]); +} +static_assert(fn<^S::j>() == 22); +static_assert(fn<^S::k>() == 26); + +// Splicing member functions. +constexpr info r_getJ = ^S::getJ; +static_assert(S{2, 4}.[:r_getJ:]() == 2); + +// Splicing static member functions. +constexpr auto rEleven = ^S::eleven; +static_assert([:rEleven:]() == 11); + +// Splicing static member template function instantiation. +constexpr auto rConst14 = ^S::constant<14>; +static_assert([:rConst14:]() == 14); + +// Splicing member function template instanstiations. +constexpr auto rgetJPlus5 = ^S::getJPlusN<5>; +static_assert(S{2, 4}.[:rgetJPlus5:]() == 7); + +// Splicing member function template instantiations with spliced objects. +constexpr S instance {1, 4}; +constexpr info rInstance = ^instance; +static_assert([:rInstance:].[:rgetJPlus5:]() == 6); +static_assert((&[:rInstance:])->[:rgetJPlus5:]() == 6); + +// Splicing dependent object in a member access expression. +template +consteval int fn3() { + return [:RObj:].k; +} +static_assert(fn3<^instance>() == 4); + +// Passing address of a spliced operand as an argument. +consteval int getMem(const S *s, int S::* mem) { + return s->*mem; +} +constexpr info rJ = ^S::j; +static_assert(getMem(&instance, &[:rJ:]) == 1); + +// Member access through a splice of a private member. +class WithPrivateBase : S {} d; +int dK = d.[:^S::k:]; + +} // namespace with_member_access + + // ====================== + // with_overridden_memfns + // ====================== + +namespace with_overridden_memfns { +struct B { consteval virtual int fn() const { return 1; } }; +struct D : B { consteval int fn() const override { return 2; } }; + +constexpr D d; +static_assert(d.[:^D::fn:]() == 2); +static_assert(d.[:^B::fn:]() == 1); +static_assert(d.[:^B:]::fn() == 1); + +// Splicing member as intermediate component of a member-access expression. +struct T { struct Inner { int v; } inner; }; +constexpr auto r_inner = ^T::inner; +constexpr T t = {{4}}; +static_assert(t.[:r_inner:].v == 4); +} // namespace with_overridden_memfns + + // ========== + // with_enums + // ========== + +namespace with_enums { +enum Enum { A, B, C }; +enum class EnumCls { A, B, C }; + +constexpr info rB = ^B, rClsB = ^EnumCls::B; +static_assert(rB != rClsB); +static_assert(int([:rB:]) == int([:rClsB:])); +static_assert(static_cast([:rClsB:]) == B); +} // namespace with_enums + + // ============= + // colon_parsing + // ============= + +// Check that parsing correctly handles successions of ':'-characters. +namespace colon_parsing { +constexpr auto r4 = ^4; +static_assert([:r4:] == 4); + +constexpr unsigned Idx = 1; +constexpr int arr[] = {1, 2, 3}; +static_assert(arr[::colon_parsing::Idx] == 2); + +constexpr info rIdx = ^Idx; +static_assert([:::colon_parsing::rIdx:] == 1); + +struct WithIndexOperator { + bool operator[:>(int); // Test interaction with ':>'-digraph (i.e., ']'). +}; +} // namespace colon_parsing + + // ======================================= + // bb_clang_p2996_issue_22_regression_test + // ======================================= + +namespace bb_clang_p2996_issue_22_regression_test { +// Issue #22 invoked a crash involving CTAD in a double templated context. +// I wasn't able to find a more minimal reproduction of the crash, but am +// including this test to prevent regression. +template +struct Cls +{ + template + struct Impl { + Impl(decltype(&[:FN:])); + }; + template + Impl(RESULT (*)(Args...)) -> Impl; +}; + +void fn(int); +static_assert(^decltype(Cls<^fn>::Impl(&fn)) == ^Cls<^fn>::Impl); +} // namespace bb_clang_p2996_issue_22_regression_test diff --git a/clang/test/Reflection/splice-namespaces.cpp b/clang/test/Reflection/splice-namespaces.cpp new file mode 100644 index 0000000000000..76021078db522 --- /dev/null +++ b/clang/test/Reflection/splice-namespaces.cpp @@ -0,0 +1,110 @@ +//===----------------------------------------------------------------------===// +// +// Copyright 2024 Bloomberg Finance L.P. +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// RUN: %clang_cc1 %s -std=c++23 -freflection + +using info = decltype(^int); + +int global_decl; + +constexpr int x = 1; + +namespace myns { + namespace inner { int y; constexpr int z = 3; } + constexpr int x = 2; +} // namespace myns + + // =========== + // idempotency + // =========== + +namespace idempotency { +namespace inner { + constexpr int x = 3; +} // namespace inner + +static_assert(&[:^:::]::global_decl == &::global_decl); +static_assert(&[:^idempotency:]::inner::x == &inner::x); +static_assert(&[:^inner:]::x == &inner::x); +} // namespace idempotency + + // ====== + // in_nns + // ====== + +namespace in_nns { +namespace Alias = ::idempotency::inner; + +constexpr info r_global = ^::; +constexpr info r_myns = ^::myns; +constexpr info r_alias = ^Alias; + +static_assert([:r_global:]::x == 1); +static_assert([:r_myns:]::x == 2); +static_assert([:r_alias:]::x == 3); + +// Splicing dependent reflection of a namespace in a nested name specifier. +template consteval int getX() { return [:R:]::x; } +static_assert(getX() == 2); +} // namespace in_nns + + // ============== + // in_alias_defns + // ============== + +namespace in_alias_defns { +constexpr info r_global = ^::; +constexpr info r_myns = ^myns; + +namespace Alias1 = [:r_myns:]; +static_assert(Alias1::x == 2); + +constexpr auto r_Alias1 = ^Alias1; +namespace Alias2 = [:r_Alias1:]; +static_assert(&myns::x == &Alias2::x); + +namespace Alias3 = [:r_global:]::idempotency::inner; +static_assert(Alias3::x == 3); + +template +consteval int XPlusY() { + namespace Alias = [:R:]; + namespace ReAlias = Alias; + namespace InnerAlias = [:R:]::inner; + namespace ReAliasInner = InnerAlias; + + return ReAlias::x + ReAliasInner::z; +} +static_assert(XPlusY<^myns>() == 5); +} // namespace in_alias_defns + + // =================== + // in_using_directives + // =================== + +namespace in_using_directives { +namespace Alias = ::myns::inner; + +constexpr info r_global = ^::; +constexpr info r_myns = ^myns; +constexpr info r_Alias = ^Alias; +void test1() { + using namespace [:r_global:]::idempotency; + static_assert(inner::x == 3); +} +void test2() { + using namespace [:r_myns:]::inner; + static_assert(&y == &myns::inner::y); +} +void test3() { + using namespace [:r_Alias:]; + static_assert(&y == &myns::inner::y); +} +} // namespace in_using_directives diff --git a/clang/test/Reflection/splice-template-arguments.cpp b/clang/test/Reflection/splice-template-arguments.cpp new file mode 100644 index 0000000000000..4aa410b88798d --- /dev/null +++ b/clang/test/Reflection/splice-template-arguments.cpp @@ -0,0 +1,271 @@ +//===----------------------------------------------------------------------===// +// +// Copyright 2024 Bloomberg Finance L.P. +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// RUN: %clang_cc1 %s -std=c++23 -freflection + +using info = decltype(^int); + +template +struct ArrayCls { T elems[Sz]; }; + +template +using Array = T[Sz]; + +template