59 changes: 56 additions & 3 deletions clang-tools-extra/include-cleaner/unittests/RecordTest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -316,7 +316,10 @@ class PragmaIncludeTest : public ::testing::Test {
};
}

TestAST build() { return TestAST(Inputs); }
TestAST build(bool ResetPragmaIncludes = true) {
if (ResetPragmaIncludes) PI = PragmaIncludes();
return TestAST(Inputs);
}

void createEmptyFiles(llvm::ArrayRef<StringRef> FileNames) {
for (llvm::StringRef File : FileNames)
Expand Down Expand Up @@ -379,6 +382,56 @@ TEST_F(PragmaIncludeTest, IWYUKeep) {
EXPECT_TRUE(PI.shouldKeep(FM.getFile("std/set").get()));
}

TEST_F(PragmaIncludeTest, AssociatedHeader) {
createEmptyFiles({"foo/main.h", "bar/main.h", "bar/other.h", "std/vector"});
auto IsKeep = [&](llvm::StringRef Name, TestAST &AST) {
return PI.shouldKeep(AST.fileManager().getFile(Name).get());
};

Inputs.FileName = "main.cc";
Inputs.ExtraArgs.push_back("-isystemstd");
{
Inputs.Code = R"cpp(
#include "foo/main.h"
#include "bar/main.h"
)cpp";
auto AST = build();
EXPECT_TRUE(IsKeep("foo/main.h", AST));
EXPECT_FALSE(IsKeep("bar/main.h", AST)) << "not first include";
}

{
Inputs.Code = R"cpp(
#include "bar/other.h"
#include "bar/main.h"
)cpp";
auto AST = build();
EXPECT_FALSE(IsKeep("bar/other.h", AST));
EXPECT_FALSE(IsKeep("bar/main.h", AST)) << "not first include";
}

{
Inputs.Code = R"cpp(
#include "foo/main.h"
#include "bar/other.h" // IWYU pragma: associated
#include <vector> // IWYU pragma: associated
)cpp";
auto AST = build();
EXPECT_TRUE(IsKeep("foo/main.h", AST));
EXPECT_TRUE(IsKeep("bar/other.h", AST));
EXPECT_TRUE(IsKeep("std/vector", AST));
}

Inputs.FileName = "vector.cc";
{
Inputs.Code = R"cpp(
#include <vector>
)cpp";
auto AST = build();
EXPECT_FALSE(IsKeep("std/vector", AST)) << "stdlib is not associated";
}
}

TEST_F(PragmaIncludeTest, IWYUPrivate) {
Inputs.Code = R"cpp(
#include "public.h"
Expand Down Expand Up @@ -577,7 +630,7 @@ TEST_F(PragmaIncludeTest, OutlivesFMAndSM) {
Inputs.MakeAction = nullptr; // Don't populate PI anymore.

// Now this build gives us a new File&Source Manager.
TestAST Processed = build();
TestAST Processed = build(/*ResetPragmaIncludes=*/false);
auto &FM = Processed.fileManager();
auto PrivateFE = FM.getFile("private.h");
assert(PrivateFE);
Expand Down Expand Up @@ -610,7 +663,7 @@ TEST_F(PragmaIncludeTest, CanRecordManyTimes) {
// any IWYU pragmas. Make sure strings from previous recordings are still
// alive.
Inputs.Code = "";
build();
build(/*ResetPragmaIncludes=*/false);
EXPECT_EQ(Public, "\"public.h\"");
}
} // namespace
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -534,7 +534,7 @@ TEST(WalkAST, Enums) {
TEST(WalkAST, InitializerList) {
testWalk(R"cpp(
namespace std {
template <typename T> struct $implicit^initializer_list {};
template <typename T> struct $implicit^initializer_list { const T *a, *b; };
})cpp",
R"cpp(
const char* s = "";
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
[
{
"directory": "$test_dir/Inputs/basic-project",
"command": "clang++ -o Calculator.o -I./include ./src/Calculator.cpp",
"file": "./src/Calculator.cpp"
},
{
"directory": "$test_dir/Inputs/basic-project",
"command": "clang++ -o Circle.o -I./include ./src/Circle.cpp",
"file": "./src/Circle.cpp"
},
{
"directory": "$test_dir/Inputs/basic-project",
"command": "clang++ -o Rectangle.o -I./include ./src/Rectangle.cpp",
"file": "./src/Rectangle.cpp"
}
]
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
#pragma once

/**
* @brief A simple calculator class.
*
* Provides basic arithmetic operations.
*/
class Calculator {
public:
/**
* @brief Adds two integers.
*
* @param a First integer.
* @param b Second integer.
* @return int The sum of a and b.
*/
int add(int a, int b);

/**
* @brief Subtracts the second integer from the first.
*
* @param a First integer.
* @param b Second integer.
* @return int The result of a - b.
*/
int subtract(int a, int b);

/**
* @brief Multiplies two integers.
*
* @param a First integer.
* @param b Second integer.
* @return int The product of a and b.
*/
int multiply(int a, int b);

/**
* @brief Divides the first integer by the second.
*
* @param a First integer.
* @param b Second integer.
* @return double The result of a / b.
* @throw std::invalid_argument if b is zero.
*/
double divide(int a, int b);
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
#pragma once

#include "Shape.h"

/**
* @brief Circle class derived from Shape.
*
* Represents a circle with a given radius.
*/
class Circle : public Shape {
public:
/**
* @brief Constructs a new Circle object.
*
* @param radius Radius of the circle.
*/
Circle(double radius);

/**
* @brief Calculates the area of the circle.
*
* @return double The area of the circle.
*/
double area() const override;

/**
* @brief Calculates the perimeter of the circle.
*
* @return double The perimeter of the circle.
*/
double perimeter() const override;

private:
double radius_; ///< Radius of the circle.
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
#pragma once

#include "Shape.h"

/**
* @brief Rectangle class derived from Shape.
*
* Represents a rectangle with a given width and height.
*/
class Rectangle : public Shape {
public:
/**
* @brief Constructs a new Rectangle object.
*
* @param width Width of the rectangle.
* @param height Height of the rectangle.
*/
Rectangle(double width, double height);

/**
* @brief Calculates the area of the rectangle.
*
* @return double The area of the rectangle.
*/
double area() const override;

/**
* @brief Calculates the perimeter of the rectangle.
*
* @return double The perimeter of the rectangle.
*/
double perimeter() const override;

private:
double width_; ///< Width of the rectangle.
double height_; ///< Height of the rectangle.
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
#pragma once

/**
* @brief Abstract base class for shapes.
*
* Provides a common interface for different types of shapes.
*/
class Shape {
public:
/**
* @brief Virtual destructor.
*/
virtual ~Shape() {}

/**
* @brief Calculates the area of the shape.
*
* @return double The area of the shape.
*/
virtual double area() const = 0;

/**
* @brief Calculates the perimeter of the shape.
*
* @return double The perimeter of the shape.
*/
virtual double perimeter() const = 0;
};


Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
#include "Calculator.h"
#include <stdexcept>

int Calculator::add(int a, int b) {
return a + b;
}

int Calculator::subtract(int a, int b) {
return a - b;
}

int Calculator::multiply(int a, int b) {
return a * b;
}

double Calculator::divide(int a, int b) {
if (b == 0) {
throw std::invalid_argument("Division by zero");
}
return static_cast<double>(a) / b;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
#include "Circle.h"

Circle::Circle(double radius) : radius_(radius) {}

double Circle::area() const {
return 3.141 * radius_ * radius_;
}

double Circle::perimeter() const {
return 3.141 * radius_;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
#include "Rectangle.h"

Rectangle::Rectangle(double width, double height)
: width_(width), height_(height) {}

double Rectangle::area() const {
return width_ * height_;
}

double Rectangle::perimeter() const {
return 2 * (width_ + height_);
}
358 changes: 358 additions & 0 deletions clang-tools-extra/test/clang-doc/basic-project.test

Large diffs are not rendered by default.

2 changes: 2 additions & 0 deletions clang-tools-extra/test/clang-doc/single-source-html.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
// RUN: clang-doc --format=html --executor=standalone %s -output=%t/docs | FileCheck %s
// CHECK: Using default asset: {{.*}}{{[\/]}}share{{[\/]}}clang
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ T max(T a, T b) {
namespace std {
template< class T >
struct initializer_list {
const T *a, *b;
initializer_list()=default;
initializer_list(T*,int){}
const T* begin() const {return nullptr;}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,11 @@
// RUN: true}}"

namespace std {
template <typename>
template <typename E>
class initializer_list
{
public:
const E *a, *b;
initializer_list() noexcept {}
};

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,10 @@
// RUN: '::std::make_pair; ::std::make_tuple; ::test::MakeSingle'}}"

namespace std {
template <typename>
template <typename E>
class initializer_list {
public:
const E *a, *b;
initializer_list() noexcept {}
};

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@

namespace std {

typedef int size_t;
typedef decltype(sizeof 0) size_t;

template<class E> class initializer_list {
public:
Expand All @@ -15,6 +15,8 @@ template<class E> class initializer_list {
using size_type = size_t;
using iterator = const E*;
using const_iterator = const E*;
iterator p;
size_t sz;
initializer_list();
size_t size() const; // number of elements
const E* begin() const; // first element
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ struct SomeClass {

namespace std {
template <typename T>
class initializer_list {};
class initializer_list { const T *a, *b; };

template <typename T>
class vector {
Expand Down
9 changes: 9 additions & 0 deletions clang-tools-extra/test/pp-trace/pp-trace-macro.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,15 @@ X
// CHECK: MacroNameTok: __STDC_UTF_32__
// CHECK-NEXT: MacroDirective: MD_Define
// CHECK: - Callback: MacroDefined
// CHECK-NEXT: MacroNameTok: __STDC_EMBED_NOT_FOUND__
// CHECK-NEXT: MacroDirective: MD_Define
// CHECK: - Callback: MacroDefined
// CHECK-NEXT: MacroNameTok: __STDC_EMBED_FOUND__
// CHECK-NEXT: MacroDirective: MD_Define
// CHECK: - Callback: MacroDefined
// CHECK-NEXT: MacroNameTok: __STDC_EMBED_EMPTY__
// CHECK-NEXT: MacroDirective: MD_Define
// CHECK: - Callback: MacroDefined
// CHECK: - Callback: MacroDefined
// CHECK-NEXT: MacroNameTok: MACRO
// CHECK-NEXT: MacroDirective: MD_Define
Expand Down
6 changes: 5 additions & 1 deletion clang/cmake/modules/AddClang.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -96,8 +96,12 @@ macro(add_clang_library name)
else()
set(LIBTYPE STATIC)
endif()
if(NOT XCODE)
if(NOT XCODE AND NOT MSVC_IDE)
# The Xcode generator doesn't handle object libraries correctly.
# The Visual Studio CMake generator does handle object libraries
# correctly, but it is preferable to list the libraries with their
# source files (instead of the object files and the source files in
# a separate target in the "Object Libraries" folder)
list(APPEND LIBTYPE OBJECT)
endif()
set_property(GLOBAL APPEND PROPERTY CLANG_STATIC_LIBS ${name})
Expand Down
7 changes: 4 additions & 3 deletions clang/docs/BoundsSafety.rst
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,9 @@
Overview
========

**NOTE:** This is a design document and the feature is not available for users yet.
Please see :doc:`BoundsSafetyImplPlans` for more details.

``-fbounds-safety`` is a C extension to enforce bounds safety to prevent
out-of-bounds (OOB) memory accesses, which remain a major source of security
vulnerabilities in C. ``-fbounds-safety`` aims to eliminate this class of bugs
Expand Down Expand Up @@ -55,9 +58,7 @@ adopt, offering these properties that make it widely adoptable in practice:
* It has a relatively low adoption cost.

This document discusses the key designs of ``-fbounds-safety``. The document is
subject to be actively updated with a more detailed specification. The
implementation plan can be found in :doc:`BoundsSafetyImplPlans`.

subject to be actively updated with a more detailed specification.

Programming Model
=================
Expand Down
65 changes: 32 additions & 33 deletions clang/docs/BoundsSafetyImplPlans.rst
Original file line number Diff line number Diff line change
Expand Up @@ -5,16 +5,39 @@ Implementation plans for ``-fbounds-safety``
.. contents::
:local:

Gradual updates with experimental flag
======================================

The feature will be implemented as a series of smaller PRs and we will guard our
implementation with an experimental flag ``-fexperimental-bounds-safety`` until
the usable model is fully available. Once the model is ready for use, we will
expose the flag ``-fbounds-safety``.

Possible patch sets
-------------------

* External bounds annotations and the (late) parsing logic.
* Internal bounds annotations (wide pointers) and their parsing logic.
* Clang code generation for wide pointers with debug information.
* Pointer cast semantics involving bounds annotations (this could be divided
into multiple sub-PRs).
* CFG analysis for pairs of related pointer and count assignments and the likes.
* Bounds check expressions in AST and the Clang code generation (this could also
be divided into multiple sub-PRs).

Proposed implementation
=======================

External bounds annotations
===========================
---------------------------

The bounds annotations are C type attributes appertaining to pointer types. If
an attribute is added to the position of a declaration attribute, e.g., ``int
*ptr __counted_by(size)``, the attribute appertains to the outermost pointer
type of the declaration (``int *``).

New sugar types
===============
---------------

An external bounds annotation creates a type sugar of the underlying pointer
types. We will introduce a new sugar type, ``DynamicBoundsPointerType`` to
Expand All @@ -29,7 +52,7 @@ overloading. However, this design requires a separate logic to walk through the
entire type hierarchy to check type compatibility of bounds annotations.

Late parsing for C
==================
------------------

A bounds annotation such as ``__counted_by(count)`` can be added to type of a
struct field declaration where count is another field of the same struct
Expand All @@ -43,7 +66,7 @@ same logic. This requires introducing late parsing logic for C/C++ type
attributes.

Internal bounds annotations
===========================
---------------------------

``__indexable`` and ``__bidi_indexable`` alter pointer representations to be
equivalent to a struct with the pointer and the corresponding bounds fields.
Expand All @@ -65,7 +88,7 @@ operations returning wide pointers. Alternatively, a new ``TEK`` and an
expression emitter dedicated to wide pointers could be introduced.

Default bounds annotations
==========================
--------------------------

The model may implicitly add ``__bidi_indexable`` or ``__single`` depending on
the context of the declaration that has the pointer type. ``__bidi_indexable``
Expand All @@ -79,7 +102,7 @@ This also requires the parser to reset the type of the declaration with the
newly created type with the right default attribute.

Promotion expression
====================
--------------------

A new expression will be introduced to represent the conversion from a pointer
with an external bounds annotation, such as ``__counted_by``, to
Expand All @@ -88,7 +111,7 @@ CastExprs because it requires an extra subexpression(s) to provide the bounds
information necessary to create a wide pointer.

Bounds check expression
=======================
-----------------------

Bounds checks are part of semantics defined in the ``-fbounds-safety`` language
model. Hence, exposing the bounds checks and other semantic actions in the AST
Expand All @@ -98,7 +121,7 @@ and has the additional sub-expressions that are necessary to perform the check
according to the kind.

Paired assignment check
=======================
-----------------------

``-fbounds-safety`` enforces that variables or fields related with the same
external bounds annotation (e.g., ``buf`` and ``count`` related with
Expand All @@ -123,7 +146,7 @@ provides a linear view of statements within each ``CFGBlock`` (Clang
``CFGBlock`` represents a single basic block in a source-level CFG).

Bounds check optimizations
==========================
--------------------------

In ``-fbounds-safety``, the Clang frontend emits run-time checks for every
memory dereference if the type system or analyses in the frontend couldn’t
Expand Down Expand Up @@ -229,27 +252,3 @@ solution.

``-fbounds-safety`` is not currently supported in C++, but we believe the
general approach would be applicable for future efforts.

Upstreaming plan
================

Gradual updates with experimental flag
--------------------------------------

The upstreaming will take place as a series of smaller PRs and we will guard our
implementation with an experimental flag ``-fexperimental-bounds-safety`` until
the usable model is fully upstreamed. Once the model is ready for use, we will
expose the flag ``-fbounds-safety``.

Possible patch sets
-------------------

* External bounds annotations and the (late) parsing logic.
* Internal bounds annotations (wide pointers) and their parsing logic.
* Clang code generation for wide pointers with debug information.
* Pointer cast semantics involving bounds annotations (this could be divided
into multiple sub-PRs).
* CFG analysis for pairs of related pointer and count assignments and the likes.
* Bounds check expressions in AST and the Clang code generation (this could also
be divided into multiple sub-PRs).

24 changes: 24 additions & 0 deletions clang/docs/LanguageExtensions.rst
Original file line number Diff line number Diff line change
Expand Up @@ -1502,6 +1502,7 @@ Attributes on Structured Bindings __cpp_structured_bindings C+
Designated initializers (N494) C99 C89
Array & element qualification (N2607) C23 C89
Attributes (N2335) C23 C89
``#embed`` (N3017) C23 C89, C++
============================================ ================================ ============= =============

Type Trait Primitives
Expand Down Expand Up @@ -5664,3 +5665,26 @@ Compiling different TUs depending on these flags (including use of
``std::hardware_destructive_interference``) with different compilers, macro
definitions, or architecture flags will lead to ODR violations and should be
avoided.
``#embed`` Parameters
=====================
``clang::offset``
-----------------
The ``clang::offset`` embed parameter may appear zero or one time in the
embed parameter sequence. Its preprocessor argument clause shall be present and
have the form:
..code-block: text
( constant-expression )
and shall be an integer constant expression. The integer constant expression
shall not evaluate to a value less than 0. The token ``defined`` shall not
appear within the constant expression.
The offset will be used when reading the contents of the embedded resource to
specify the starting offset to begin embedding from. The resources is treated
as being empty if the specified offset is larger than the number of bytes in
the resource. The offset will be applied *before* any ``limit`` parameters are
applied.
36 changes: 36 additions & 0 deletions clang/docs/PointerAuthentication.rst
Original file line number Diff line number Diff line change
Expand Up @@ -328,6 +328,23 @@ be done in a single instruction with an immediate integer.
``pointer`` must have pointer type, and ``integer`` must have integer type. The
result has type ``ptrauth_extra_data_t``.

``ptrauth_string_discriminator``
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

.. code-block:: c
ptrauth_string_discriminator(string)
Compute a constant discriminator from the given string.

``string`` must be a string literal of ``char`` character type. The result has
type ``ptrauth_extra_data_t``.

The result value is never zero and always within range for both the
``__ptrauth`` qualifier and ``ptrauth_blend_discriminator``.

This can be used in constant expressions.

``ptrauth_strip``
^^^^^^^^^^^^^^^^^

Expand All @@ -339,6 +356,25 @@ Given that ``signedPointer`` matches the layout for signed pointers signed with
the given key, extract the raw pointer from it. This operation does not trap
and cannot fail, even if the pointer is not validly signed.

``ptrauth_sign_constant``
^^^^^^^^^^^^^^^^^^^^^^^^^

.. code-block:: c
ptrauth_sign_constant(pointer, key, discriminator)
Return a signed pointer for a constant address in a manner which guarantees
a non-attackable sequence.

``pointer`` must be a constant expression of pointer type which evaluates to
a non-null pointer.
``key`` must be a constant expression of type ``ptrauth_key``.
``discriminator`` must be a constant expression of pointer or integer type;
if an integer, it will be coerced to ``ptrauth_extra_data_t``.
The result will have the same type as ``pointer``.

This can be used in constant expressions.

``ptrauth_sign_unauthenticated``
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

Expand Down
36 changes: 31 additions & 5 deletions clang/docs/ReleaseNotes.rst
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,13 @@ ABI Changes in This Version
- Fixed Microsoft calling convention when returning classes that have a deleted
copy assignment operator. Such a class should be returned indirectly.

- Fixed Microsoft name mangling for auto non-type template arguments of pointer
type for MSVC 1920+. This change resolves incompatibilities with code compiled
by MSVC 1920+ but will introduce incompatibilities with code compiled by
earlier versions of Clang unless such code is built with the compiler option
`-fms-compatibility-version=19.14` to imitate the MSVC 1914 mangling behavior.


AST Dumping Potentially Breaking Changes
----------------------------------------

Expand Down Expand Up @@ -150,6 +157,15 @@ here. Generic improvements to Clang as a whole or to its underlying
infrastructure are described first, followed by language-specific
sections with improvements to Clang's support for those languages.

- The ``\par`` documentation comment command now supports an optional
argument, which denotes the header of the paragraph started by
an instance of the ``\par`` command comment. The implementation
of the argument handling matches its semantics
`in Doxygen <https://www.doxygen.nl/manual/commands.html#cmdpar>`.
Namely, any text on the same line as the ``\par`` command will become
a header for the paragaph, and if there is no text then the command
will start a new paragraph.

C++ Language Changes
--------------------
- C++17 support is now completed, with the enablement of the
Expand Down Expand Up @@ -271,6 +287,9 @@ Resolutions to C++ Defect Reports
- P0522 implementation is enabled by default in all language versions, and
provisional wording for CWG2398 is implemented.

- Clang now performs type-only lookup for the name in ``using enum`` declaration.
(`CWG2877: Type-only lookup for using-enum-declarator <https://cplusplus.github.io/CWG/issues/2877.html>`_).

- Clang now requires a template argument list after a template keyword.
(`CWG96: Syntactic disambiguation using the template keyword <https://cplusplus.github.io/CWG/issues/96.html>`_).

Expand Down Expand Up @@ -589,6 +608,10 @@ Improvements to Clang's diagnostics
- Clang no longer emits a "declared here" note for a builtin function that has no declaration in source.
Fixes #GH93369.

- Clang now diagnoses unsupported class declarations for ``std::initializer_list<E>`` when they are
used rather than when they are needed for constant evaluation or when code is generated for them.
The check is now stricter to prevent crashes for some unsupported declarations (Fixes #GH95495).

Improvements to Clang's time-trace
----------------------------------

Expand Down Expand Up @@ -695,6 +718,8 @@ Bug Fixes in This Version
- Correctly reject declarations where a statement is required in C.
Fixes #GH92775

- Fixed `static_cast` to array of unknown bound. Fixes (#GH62863).

Bug Fixes to Compiler Builtins
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

Expand Down Expand Up @@ -887,7 +912,8 @@ Bug Fixes to C++ Support
between the addresses of two labels (a GNU extension) to a pointer within a constant expression. (#GH95366).
- Fix immediate escalation bugs in the presence of dependent call arguments. (#GH94935)
- Clang now diagnoses explicit specializations with storage class specifiers in all contexts.

- Fix an assertion failure caused by parsing a lambda used as a default argument for the value of a
forward-declared class. (#GH93512).

Bug Fixes to AST Handling
^^^^^^^^^^^^^^^^^^^^^^^^^
Expand Down Expand Up @@ -1020,10 +1046,10 @@ AIX Support
WebAssembly Support
^^^^^^^^^^^^^^^^^^^

The -mcpu=generic configuration now enables multivalue feature, which is
standardized and available in all major engines. Enabling multivalue here only
enables the language feature but does not turn on the multivalue ABI (this
enables non-ABI uses of multivalue, like exnref).
The -mcpu=generic configuration now enables multivalue and reference-types.
These proposals are standardized and available in all major engines. Enabling
multivalue here only enables the language feature but does not turn on the
multivalue ABI (this enables non-ABI uses of multivalue, like exnref).

AVR Support
^^^^^^^^^^^
Expand Down
38 changes: 19 additions & 19 deletions clang/docs/analyzer/checkers.rst
Original file line number Diff line number Diff line change
Expand Up @@ -416,6 +416,25 @@ around, such as ``std::string_view``.
// note: inner buffer of 'std::string' deallocated by call to destructor
}
.. _cplusplus-Move:
cplusplus.Move (C++)
""""""""""""""""""""
Method calls on a moved-from object and copying a moved-from object will be reported.
.. code-block:: cpp
struct A {
void foo() {}
};
void f() {
A a;
A b = std::move(a); // note: 'a' became 'moved-from' here
a.foo(); // warn: method call on a 'moved-from' object 'a'
}
.. _cplusplus-NewDelete:
cplusplus.NewDelete (C++)
Expand Down Expand Up @@ -2584,25 +2603,6 @@ Check for use of iterators of different containers where iterators of the same c
// expected
}
.. _alpha-cplusplus-MisusedMovedObject:
alpha.cplusplus.MisusedMovedObject (C++)
""""""""""""""""""""""""""""""""""""""""
Method calls on a moved-from object and copying a moved-from object will be reported.
.. code-block:: cpp
struct A {
void foo() {}
};
void f() {
A a;
A b = std::move(a); // note: 'a' became 'moved-from' here
a.foo(); // warn: method call on a 'moved-from' object 'a'
}
.. _alpha-cplusplus-SmartPtr:
alpha.cplusplus.SmartPtr (C++)
Expand Down
2 changes: 1 addition & 1 deletion clang/include/clang/AST/ASTUnresolvedSet.h
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ class ASTUnresolvedSet {
}

void addLazyDecl(ASTContext &C, GlobalDeclID ID, AccessSpecifier AS) {
Decls.push_back(DeclAccessPair::makeLazy(ID.get(), AS), C);
Decls.push_back(DeclAccessPair::makeLazy(ID.getRawValue(), AS), C);
}

/// Replaces the given declaration with the new one, once.
Expand Down
4 changes: 4 additions & 0 deletions clang/include/clang/AST/CommentCommandTraits.h
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,10 @@ struct CommandInfo {
LLVM_PREFERRED_TYPE(bool)
unsigned IsHeaderfileCommand : 1;

/// True if this is a \\par command.
LLVM_PREFERRED_TYPE(bool)
unsigned IsParCommand : 1;

/// True if we don't want to warn about this command being passed an empty
/// paragraph. Meaningful only for block commands.
LLVM_PREFERRED_TYPE(bool)
Expand Down
3 changes: 2 additions & 1 deletion clang/include/clang/AST/CommentCommands.td
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ class Command<string name> {
bit IsThrowsCommand = 0;
bit IsDeprecatedCommand = 0;
bit IsHeaderfileCommand = 0;
bit IsParCommand = 0;

bit IsEmptyParagraphAllowed = 0;

Expand Down Expand Up @@ -156,7 +157,7 @@ def Date : BlockCommand<"date">;
def Invariant : BlockCommand<"invariant">;
def Li : BlockCommand<"li">;
def Note : BlockCommand<"note">;
def Par : BlockCommand<"par">;
def Par : BlockCommand<"par"> { let IsParCommand = 1; let NumArgs = 1; }
def Post : BlockCommand<"post">;
def Pre : BlockCommand<"pre">;
def Remark : BlockCommand<"remark">;
Expand Down
4 changes: 3 additions & 1 deletion clang/include/clang/AST/CommentParser.h
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,9 @@ class Parser {
ArrayRef<Comment::Argument>
parseThrowCommandArgs(TextTokenRetokenizer &Retokenizer, unsigned NumArgs);

ArrayRef<Comment::Argument>
parseParCommandArgs(TextTokenRetokenizer &Retokenizer, unsigned NumArgs);

BlockCommandComment *parseBlockCommand();
InlineCommandComment *parseInlineCommand();

Expand All @@ -123,4 +126,3 @@ class Parser {
} // end namespace clang

#endif

52 changes: 40 additions & 12 deletions clang/include/clang/AST/DeclID.h
Original file line number Diff line number Diff line change
Expand Up @@ -116,12 +116,8 @@ class DeclIDBase {
DeclIDBase() : ID(PREDEF_DECL_NULL_ID) {}
explicit DeclIDBase(DeclID ID) : ID(ID) {}

explicit DeclIDBase(unsigned LocalID, unsigned ModuleFileIndex) {
ID = (DeclID)LocalID | ((DeclID)ModuleFileIndex << 32);
}

public:
DeclID get() const { return ID; }
DeclID getRawValue() const { return ID; }

explicit operator DeclID() const { return ID; }

Expand All @@ -135,12 +131,33 @@ class DeclIDBase {

unsigned getLocalDeclIndex() const;

// The DeclID may be compared with predefined decl ID.
friend bool operator==(const DeclIDBase &LHS, const DeclID &RHS) {
return LHS.ID == RHS;
}
friend bool operator!=(const DeclIDBase &LHS, const DeclID &RHS) {
return !operator==(LHS, RHS);
}
friend bool operator<(const DeclIDBase &LHS, const DeclID &RHS) {
return LHS.ID < RHS;
}
friend bool operator<=(const DeclIDBase &LHS, const DeclID &RHS) {
return LHS.ID <= RHS;
}
friend bool operator>(const DeclIDBase &LHS, const DeclID &RHS) {
return LHS.ID > RHS;
}
friend bool operator>=(const DeclIDBase &LHS, const DeclID &RHS) {
return LHS.ID >= RHS;
}

friend bool operator==(const DeclIDBase &LHS, const DeclIDBase &RHS) {
return LHS.ID == RHS.ID;
}
friend bool operator!=(const DeclIDBase &LHS, const DeclIDBase &RHS) {
return LHS.ID != RHS.ID;
}

// We may sort the decl ID.
friend bool operator<(const DeclIDBase &LHS, const DeclIDBase &RHS) {
return LHS.ID < RHS.ID;
Expand All @@ -159,16 +176,27 @@ class DeclIDBase {
DeclID ID;
};

class ASTWriter;
class ASTReader;
namespace serialization {
class ModuleFile;
} // namespace serialization

class LocalDeclID : public DeclIDBase {
using Base = DeclIDBase;

public:
LocalDeclID() : Base() {}
LocalDeclID(PredefinedDeclIDs ID) : Base(ID) {}
explicit LocalDeclID(DeclID ID) : Base(ID) {}

explicit LocalDeclID(unsigned LocalID, unsigned ModuleFileIndex)
: Base(LocalID, ModuleFileIndex) {}
// Every Decl ID is a local decl ID to the module being writing in ASTWriter.
friend class ASTWriter;
friend class GlobalDeclID;

public:
LocalDeclID() : Base() {}

static LocalDeclID get(ASTReader &Reader, serialization::ModuleFile &MF,
DeclID ID);

LocalDeclID &operator++() {
++ID;
Expand All @@ -189,8 +217,8 @@ class GlobalDeclID : public DeclIDBase {
GlobalDeclID() : Base() {}
explicit GlobalDeclID(DeclID ID) : Base(ID) {}

explicit GlobalDeclID(unsigned LocalID, unsigned ModuleFileIndex)
: Base(LocalID, ModuleFileIndex) {}
explicit GlobalDeclID(unsigned ModuleFileIndex, unsigned LocalID)
: Base((DeclID)ModuleFileIndex << 32 | (DeclID)LocalID) {}

// For DeclIDIterator<GlobalDeclID> to be able to convert a GlobalDeclID
// to a LocalDeclID.
Expand Down Expand Up @@ -235,7 +263,7 @@ template <> struct DenseMapInfo<clang::GlobalDeclID> {
// In GlobalDeclID's case, it is pretty common that the lower 32 bits can
// be same.
// FIXME: Remove this when we fix the underlying issue.
return llvm::hash_value(Key.get());
return llvm::hash_value(Key.getRawValue());
}

static bool isEqual(const GlobalDeclID &L, const GlobalDeclID &R) {
Expand Down
158 changes: 158 additions & 0 deletions clang/include/clang/AST/Expr.h
Original file line number Diff line number Diff line change
Expand Up @@ -4799,6 +4799,164 @@ class SourceLocExpr final : public Expr {
friend class ASTStmtReader;
};

/// Stores data related to a single #embed directive.
struct EmbedDataStorage {
StringLiteral *BinaryData;
size_t getDataElementCount() const { return BinaryData->getByteLength(); }
};

/// Represents a reference to #emded data. By default, this references the whole
/// range. Otherwise it represents a subrange of data imported by #embed
/// directive. Needed to handle nested initializer lists with #embed directives.
/// Example:
/// struct S {
/// int x, y;
/// };
///
/// struct T {
/// int x[2];
/// struct S s
/// };
///
/// struct T t[] = {
/// #embed "data" // data contains 10 elements;
/// };
///
/// The resulting semantic form of initializer list will contain (EE stands
/// for EmbedExpr):
/// { {EE(first two data elements), {EE(3rd element), EE(4th element) }},
/// { {EE(5th and 6th element), {EE(7th element), EE(8th element) }},
/// { {EE(9th and 10th element), { zeroinitializer }}}
///
/// EmbedExpr inside of a semantic initializer list and referencing more than
/// one element can only appear for arrays of scalars.
class EmbedExpr final : public Expr {
SourceLocation EmbedKeywordLoc;
IntegerLiteral *FakeChildNode = nullptr;
const ASTContext *Ctx = nullptr;
EmbedDataStorage *Data;
unsigned Begin = 0;
unsigned NumOfElements;

public:
EmbedExpr(const ASTContext &Ctx, SourceLocation Loc, EmbedDataStorage *Data,
unsigned Begin, unsigned NumOfElements);
explicit EmbedExpr(EmptyShell Empty) : Expr(SourceLocExprClass, Empty) {}

SourceLocation getLocation() const { return EmbedKeywordLoc; }
SourceLocation getBeginLoc() const { return EmbedKeywordLoc; }
SourceLocation getEndLoc() const { return EmbedKeywordLoc; }

StringLiteral *getDataStringLiteral() const { return Data->BinaryData; }
EmbedDataStorage *getData() const { return Data; }

unsigned getStartingElementPos() const { return Begin; }
size_t getDataElementCount() const { return NumOfElements; }

// Allows accessing every byte of EmbedExpr data and iterating over it.
// An Iterator knows the EmbedExpr that it refers to, and an offset value
// within the data.
// Dereferencing an Iterator results in construction of IntegerLiteral AST
// node filled with byte of data of the corresponding EmbedExpr within offset
// that the Iterator currently has.
template <bool Const>
class ChildElementIter
: public llvm::iterator_facade_base<
ChildElementIter<Const>, std::random_access_iterator_tag,
std::conditional_t<Const, const IntegerLiteral *,
IntegerLiteral *>> {
friend class EmbedExpr;

EmbedExpr *EExpr = nullptr;
unsigned long long CurOffset = ULLONG_MAX;
using BaseTy = typename ChildElementIter::iterator_facade_base;

ChildElementIter(EmbedExpr *E) : EExpr(E) {
if (E)
CurOffset = E->getStartingElementPos();
}

public:
ChildElementIter() : CurOffset(ULLONG_MAX) {}
typename BaseTy::reference operator*() const {
assert(EExpr && CurOffset != ULLONG_MAX &&
"trying to dereference an invalid iterator");
IntegerLiteral *N = EExpr->FakeChildNode;
StringRef DataRef = EExpr->Data->BinaryData->getBytes();
N->setValue(*EExpr->Ctx,
llvm::APInt(N->getValue().getBitWidth(), DataRef[CurOffset],
N->getType()->isSignedIntegerType()));
// We want to return a reference to the fake child node in the
// EmbedExpr, not the local variable N.
return const_cast<typename BaseTy::reference>(EExpr->FakeChildNode);
}
typename BaseTy::pointer operator->() const { return **this; }
using BaseTy::operator++;
ChildElementIter &operator++() {
assert(EExpr && "trying to increment an invalid iterator");
assert(CurOffset != ULLONG_MAX &&
"Already at the end of what we can iterate over");
if (++CurOffset >=
EExpr->getDataElementCount() + EExpr->getStartingElementPos()) {
CurOffset = ULLONG_MAX;
EExpr = nullptr;
}
return *this;
}
bool operator==(ChildElementIter Other) const {
return (EExpr == Other.EExpr && CurOffset == Other.CurOffset);
}
}; // class ChildElementIter

public:
using fake_child_range = llvm::iterator_range<ChildElementIter<false>>;
using const_fake_child_range = llvm::iterator_range<ChildElementIter<true>>;

fake_child_range underlying_data_elements() {
return fake_child_range(ChildElementIter<false>(this),
ChildElementIter<false>());
}

const_fake_child_range underlying_data_elements() const {
return const_fake_child_range(
ChildElementIter<true>(const_cast<EmbedExpr *>(this)),
ChildElementIter<true>());
}

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() == EmbedExprClass;
}

ChildElementIter<false> begin() { return ChildElementIter<false>(this); }

ChildElementIter<true> begin() const {
return ChildElementIter<true>(const_cast<EmbedExpr *>(this));
}

template <typename Call, typename... Targs>
bool doForEachDataElement(Call &&C, unsigned &StartingIndexInArray,
Targs &&...Fargs) const {
for (auto It : underlying_data_elements()) {
if (!std::invoke(std::forward<Call>(C), const_cast<IntegerLiteral *>(It),
StartingIndexInArray, std::forward<Targs>(Fargs)...))
return false;
StartingIndexInArray++;
}
return true;
}

private:
friend class ASTStmtReader;
};

/// Describes an C or C++ initializer list.
///
/// InitListExpr describes an initializer list, which can be used to
Expand Down
5 changes: 5 additions & 0 deletions clang/include/clang/AST/RecursiveASTVisitor.h
Original file line number Diff line number Diff line change
Expand Up @@ -2864,6 +2864,11 @@ DEF_TRAVERSE_STMT(ShuffleVectorExpr, {})
DEF_TRAVERSE_STMT(ConvertVectorExpr, {})
DEF_TRAVERSE_STMT(StmtExpr, {})
DEF_TRAVERSE_STMT(SourceLocExpr, {})
DEF_TRAVERSE_STMT(EmbedExpr, {
for (IntegerLiteral *IL : S->underlying_data_elements()) {
TRY_TO_TRAVERSE_OR_ENQUEUE_STMT(IL);
}
})

DEF_TRAVERSE_STMT(UnresolvedLookupExpr, {
TRY_TO(TraverseNestedNameSpecifierLoc(S->getQualifierLoc()));
Expand Down
1 change: 1 addition & 0 deletions clang/include/clang/AST/TextNodeDumper.h
Original file line number Diff line number Diff line change
Expand Up @@ -409,6 +409,7 @@ class TextNodeDumper
void VisitHLSLBufferDecl(const HLSLBufferDecl *D);
void VisitOpenACCConstructStmt(const OpenACCConstructStmt *S);
void VisitOpenACCLoopConstruct(const OpenACCLoopConstruct *S);
void VisitEmbedExpr(const EmbedExpr *S);
};

} // namespace clang
Expand Down
182 changes: 137 additions & 45 deletions clang/include/clang/Analysis/FlowSensitive/DataflowAnalysis.h
Original file line number Diff line number Diff line change
Expand Up @@ -178,13 +178,50 @@ template <typename LatticeT> struct DataflowAnalysisState {
Environment Env;
};

/// A callback to be called with the state before or after visiting a CFG
/// element.
template <typename AnalysisT>
using CFGEltCallback = std::function<void(
const CFGElement &,
const DataflowAnalysisState<typename AnalysisT::Lattice> &)>;

/// A pair of callbacks to be called with the state before and after visiting a
/// CFG element.
/// Either or both of the callbacks may be null.
template <typename AnalysisT> struct CFGEltCallbacks {
CFGEltCallback<AnalysisT> Before;
CFGEltCallback<AnalysisT> After;
};

/// A callback for performing diagnosis on a CFG element, called with the state
/// before or after visiting that CFG element. Returns a list of diagnostics
/// to emit (if any).
template <typename AnalysisT, typename Diagnostic>
using DiagnosisCallback = llvm::function_ref<llvm::SmallVector<Diagnostic>(
const CFGElement &, ASTContext &,
const TransferStateForDiagnostics<typename AnalysisT::Lattice> &)>;

/// A pair of callbacks for performing diagnosis on a CFG element, called with
/// the state before and after visiting that CFG element.
/// Either or both of the callbacks may be null.
template <typename AnalysisT, typename Diagnostic> struct DiagnosisCallbacks {
DiagnosisCallback<AnalysisT, Diagnostic> Before;
DiagnosisCallback<AnalysisT, Diagnostic> After;
};

/// Default for the maximum number of SAT solver iterations during analysis.
inline constexpr std::int64_t kDefaultMaxSATIterations = 1'000'000'000;

/// Default for the maximum number of block visits during analysis.
inline constexpr std::int32_t kDefaultMaxBlockVisits = 20'000;

/// Performs dataflow analysis and returns a mapping from basic block IDs to
/// dataflow analysis states that model the respective basic blocks. The
/// returned vector, if any, will have the same size as the number of CFG
/// blocks, with indices corresponding to basic block IDs. Returns an error if
/// the dataflow analysis cannot be performed successfully. Otherwise, calls
/// `PostVisitCFG` on each CFG element with the final analysis results at that
/// program point.
/// `PostAnalysisCallbacks` on each CFG element with the final analysis results
/// before and after that program point.
///
/// `MaxBlockVisits` caps the number of block visits during analysis. See
/// `runTypeErasedDataflowAnalysis` for a full description. The default value is
Expand All @@ -194,30 +231,40 @@ template <typename LatticeT> struct DataflowAnalysisState {
template <typename AnalysisT>
llvm::Expected<std::vector<
std::optional<DataflowAnalysisState<typename AnalysisT::Lattice>>>>
runDataflowAnalysis(
const AdornedCFG &ACFG, AnalysisT &Analysis, const Environment &InitEnv,
std::function<void(const CFGElement &, const DataflowAnalysisState<
typename AnalysisT::Lattice> &)>
PostVisitCFG = nullptr,
std::int32_t MaxBlockVisits = 20'000) {
std::function<void(const CFGElement &,
const TypeErasedDataflowAnalysisState &)>
PostVisitCFGClosure = nullptr;
if (PostVisitCFG) {
PostVisitCFGClosure = [&PostVisitCFG](
const CFGElement &Element,
const TypeErasedDataflowAnalysisState &State) {
auto *Lattice =
llvm::any_cast<typename AnalysisT::Lattice>(&State.Lattice.Value);
// FIXME: we should not be copying the environment here!
// Ultimately the PostVisitCFG only gets a const reference anyway.
PostVisitCFG(Element, DataflowAnalysisState<typename AnalysisT::Lattice>{
*Lattice, State.Env.fork()});
};
runDataflowAnalysis(const AdornedCFG &ACFG, AnalysisT &Analysis,
const Environment &InitEnv,
CFGEltCallbacks<AnalysisT> PostAnalysisCallbacks,
std::int32_t MaxBlockVisits = kDefaultMaxBlockVisits) {
CFGEltCallbacksTypeErased TypeErasedCallbacks;
if (PostAnalysisCallbacks.Before) {
TypeErasedCallbacks.Before =
[&PostAnalysisCallbacks](const CFGElement &Element,
const TypeErasedDataflowAnalysisState &State) {
auto *Lattice =
llvm::any_cast<typename AnalysisT::Lattice>(&State.Lattice.Value);
// FIXME: we should not be copying the environment here!
// Ultimately the `CFGEltCallback` only gets a const reference anyway.
PostAnalysisCallbacks.Before(
Element, DataflowAnalysisState<typename AnalysisT::Lattice>{
*Lattice, State.Env.fork()});
};
}
if (PostAnalysisCallbacks.After) {
TypeErasedCallbacks.After =
[&PostAnalysisCallbacks](const CFGElement &Element,
const TypeErasedDataflowAnalysisState &State) {
auto *Lattice =
llvm::any_cast<typename AnalysisT::Lattice>(&State.Lattice.Value);
// FIXME: we should not be copying the environment here!
// Ultimately the `CFGEltCallback` only gets a const reference anyway.
PostAnalysisCallbacks.After(
Element, DataflowAnalysisState<typename AnalysisT::Lattice>{
*Lattice, State.Env.fork()});
};
}

auto TypeErasedBlockStates = runTypeErasedDataflowAnalysis(
ACFG, Analysis, InitEnv, PostVisitCFGClosure, MaxBlockVisits);
ACFG, Analysis, InitEnv, TypeErasedCallbacks, MaxBlockVisits);
if (!TypeErasedBlockStates)
return TypeErasedBlockStates.takeError();

Expand All @@ -239,6 +286,22 @@ runDataflowAnalysis(
return std::move(BlockStates);
}

/// Overload that takes only one post-analysis callback, which is run on the
/// state after visiting the `CFGElement`. This is provided for backwards
/// compatibility; new callers should call the overload taking `CFGEltCallbacks`
/// instead.
template <typename AnalysisT>
llvm::Expected<std::vector<
std::optional<DataflowAnalysisState<typename AnalysisT::Lattice>>>>
runDataflowAnalysis(
const AdornedCFG &ACFG, AnalysisT &Analysis, const Environment &InitEnv,
CFGEltCallback<AnalysisT> PostAnalysisCallbackAfterElt = nullptr,
std::int32_t MaxBlockVisits = kDefaultMaxBlockVisits) {
return runDataflowAnalysis(ACFG, Analysis, InitEnv,
{nullptr, PostAnalysisCallbackAfterElt},
MaxBlockVisits);
}

// Create an analysis class that is derived from `DataflowAnalysis`. This is an
// SFINAE adapter that allows us to call two different variants of constructor
// (either with or without the optional `Environment` parameter).
Expand Down Expand Up @@ -271,14 +334,11 @@ auto createAnalysis(ASTContext &ASTCtx, Environment &Env)
/// `runDataflowAnalysis` for a full description and explanation of the default
/// value.
template <typename AnalysisT, typename Diagnostic>
llvm::Expected<llvm::SmallVector<Diagnostic>> diagnoseFunction(
const FunctionDecl &FuncDecl, ASTContext &ASTCtx,
llvm::function_ref<llvm::SmallVector<Diagnostic>(
const CFGElement &, ASTContext &,
const TransferStateForDiagnostics<typename AnalysisT::Lattice> &)>
Diagnoser,
std::int64_t MaxSATIterations = 1'000'000'000,
std::int32_t MaxBlockVisits = 20'000) {
llvm::Expected<llvm::SmallVector<Diagnostic>>
diagnoseFunction(const FunctionDecl &FuncDecl, ASTContext &ASTCtx,
DiagnosisCallbacks<AnalysisT, Diagnostic> Diagnoser,
std::int64_t MaxSATIterations = kDefaultMaxSATIterations,
std::int32_t MaxBlockVisits = kDefaultMaxBlockVisits) {
llvm::Expected<AdornedCFG> Context = AdornedCFG::build(FuncDecl);
if (!Context)
return Context.takeError();
Expand All @@ -288,21 +348,38 @@ llvm::Expected<llvm::SmallVector<Diagnostic>> diagnoseFunction(
Environment Env(AnalysisContext, FuncDecl);
AnalysisT Analysis = createAnalysis<AnalysisT>(ASTCtx, Env);
llvm::SmallVector<Diagnostic> Diagnostics;
CFGEltCallbacksTypeErased PostAnalysisCallbacks;
if (Diagnoser.Before) {
PostAnalysisCallbacks.Before =
[&ASTCtx, &Diagnoser,
&Diagnostics](const CFGElement &Elt,
const TypeErasedDataflowAnalysisState &State) mutable {
auto EltDiagnostics = Diagnoser.Before(
Elt, ASTCtx,
TransferStateForDiagnostics<typename AnalysisT::Lattice>(
llvm::any_cast<const typename AnalysisT::Lattice &>(
State.Lattice.Value),
State.Env));
llvm::move(EltDiagnostics, std::back_inserter(Diagnostics));
};
}
if (Diagnoser.After) {
PostAnalysisCallbacks.After =
[&ASTCtx, &Diagnoser,
&Diagnostics](const CFGElement &Elt,
const TypeErasedDataflowAnalysisState &State) mutable {
auto EltDiagnostics = Diagnoser.After(
Elt, ASTCtx,
TransferStateForDiagnostics<typename AnalysisT::Lattice>(
llvm::any_cast<const typename AnalysisT::Lattice &>(
State.Lattice.Value),
State.Env));
llvm::move(EltDiagnostics, std::back_inserter(Diagnostics));
};
}
if (llvm::Error Err =
runTypeErasedDataflowAnalysis(
*Context, Analysis, Env,
[&ASTCtx, &Diagnoser, &Diagnostics](
const CFGElement &Elt,
const TypeErasedDataflowAnalysisState &State) mutable {
auto EltDiagnostics = Diagnoser(
Elt, ASTCtx,
TransferStateForDiagnostics<typename AnalysisT::Lattice>(
llvm::any_cast<const typename AnalysisT::Lattice &>(
State.Lattice.Value),
State.Env));
llvm::move(EltDiagnostics, std::back_inserter(Diagnostics));
},
MaxBlockVisits)
runTypeErasedDataflowAnalysis(*Context, Analysis, Env,
PostAnalysisCallbacks, MaxBlockVisits)
.takeError())
return std::move(Err);

Expand All @@ -313,6 +390,21 @@ llvm::Expected<llvm::SmallVector<Diagnostic>> diagnoseFunction(
return Diagnostics;
}

/// Overload that takes only one diagnosis callback, which is run on the state
/// after visiting the `CFGElement`. This is provided for backwards
/// compatibility; new callers should call the overload taking
/// `DiagnosisCallbacks` instead.
template <typename AnalysisT, typename Diagnostic>
llvm::Expected<llvm::SmallVector<Diagnostic>>
diagnoseFunction(const FunctionDecl &FuncDecl, ASTContext &ASTCtx,
DiagnosisCallback<AnalysisT, Diagnostic> Diagnoser,
std::int64_t MaxSATIterations = kDefaultMaxSATIterations,
std::int32_t MaxBlockVisits = kDefaultMaxBlockVisits) {
DiagnosisCallbacks<AnalysisT, Diagnostic> Callbacks = {nullptr, Diagnoser};
return diagnoseFunction(FuncDecl, ASTCtx, Callbacks, MaxSATIterations,
MaxBlockVisits);
}

/// Abstract base class for dataflow "models": reusable analysis components that
/// model a particular aspect of program semantics in the `Environment`. For
/// example, a model may capture a type and its related functions.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -132,12 +132,25 @@ struct TypeErasedDataflowAnalysisState {
}
};

/// A callback to be called with the state before or after visiting a CFG
/// element.
using CFGEltCallbackTypeErased = std::function<void(
const CFGElement &, const TypeErasedDataflowAnalysisState &)>;

/// A pair of callbacks to be called with the state before and after visiting a
/// CFG element.
/// Either or both of the callbacks may be null.
struct CFGEltCallbacksTypeErased {
CFGEltCallbackTypeErased Before;
CFGEltCallbackTypeErased After;
};

/// Performs dataflow analysis and returns a mapping from basic block IDs to
/// dataflow analysis states that model the respective basic blocks. Indices of
/// the returned vector correspond to basic block IDs. Returns an error if the
/// dataflow analysis cannot be performed successfully. Otherwise, calls
/// `PostVisitCFG` on each CFG element with the final analysis results at that
/// program point.
/// `PostAnalysisCallbacks` on each CFG element with the final analysis results
/// before and after that program point.
///
/// `MaxBlockVisits` caps the number of block visits during analysis. It doesn't
/// distinguish between repeat visits to the same block and visits to distinct
Expand All @@ -148,9 +161,7 @@ llvm::Expected<std::vector<std::optional<TypeErasedDataflowAnalysisState>>>
runTypeErasedDataflowAnalysis(
const AdornedCFG &ACFG, TypeErasedDataflowAnalysis &Analysis,
const Environment &InitEnv,
std::function<void(const CFGElement &,
const TypeErasedDataflowAnalysisState &)>
PostVisitCFG,
const CFGEltCallbacksTypeErased &PostAnalysisCallbacks,
std::int32_t MaxBlockVisits);

} // namespace dataflow
Expand Down
1 change: 1 addition & 0 deletions clang/include/clang/Basic/Builtins.def
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@
// q -> Scalable vector, followed by the number of elements and the base type.
// Q -> target builtin type, followed by a character to distinguish the builtin type
// Qa -> AArch64 svcount_t builtin type.
// Qb -> AMDGPU __amdgpu_buffer_rsrc_t builtin type.
// E -> ext_vector, followed by the number of elements and the base type.
// X -> _Complex, followed by the base type.
// Y -> ptrdiff_t
Expand Down
12 changes: 12 additions & 0 deletions clang/include/clang/Basic/Builtins.td
Original file line number Diff line number Diff line change
Expand Up @@ -4393,6 +4393,12 @@ def PtrauthSignUnauthenticated : Builtin {
let Prototype = "void*(void*,int,void*)";
}

def PtrauthSignConstant : Builtin {
let Spellings = ["__builtin_ptrauth_sign_constant"];
let Attributes = [CustomTypeChecking, NoThrow, Const, Constexpr];
let Prototype = "void*(void*,int,void*)";
}

def PtrauthSignGenericData : Builtin {
let Spellings = ["__builtin_ptrauth_sign_generic_data"];
let Attributes = [CustomTypeChecking, NoThrow, Const];
Expand All @@ -4411,6 +4417,12 @@ def PtrauthAuth : Builtin {
let Prototype = "void*(void*,int,void*)";
}

def PtrauthStringDiscriminator : Builtin {
let Spellings = ["__builtin_ptrauth_string_discriminator"];
let Attributes = [NoThrow, Const, Constexpr];
let Prototype = "size_t(char const*)";
}

// OpenCL v2.0 s6.13.16, s9.17.3.5 - Pipe functions.
// We need the generic prototype, since the packet type could be anything.
def ReadPipe : OCLPipeLangBuiltin {
Expand Down
2 changes: 1 addition & 1 deletion clang/include/clang/Basic/BuiltinsAArch64.def
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ TARGET_BUILTIN(__builtin_arm_stg, "vv*", "t", "mte")
TARGET_BUILTIN(__builtin_arm_subp, "Uiv*v*", "t", "mte")

// SME state function
BUILTIN(__builtin_arm_get_sme_state, "vULi*ULi*", "n")
BUILTIN(__builtin_arm_get_sme_state, "vWUi*WUi*", "n")

// Memory Operations
TARGET_BUILTIN(__builtin_arm_mops_memset_tag, "v*v*iz", "", "mte,mops")
Expand Down
2 changes: 2 additions & 0 deletions clang/include/clang/Basic/BuiltinsAMDGPU.def
Original file line number Diff line number Diff line change
Expand Up @@ -148,6 +148,8 @@ BUILTIN(__builtin_amdgcn_qsad_pk_u16_u8, "WUiWUiUiWUi", "nc")
BUILTIN(__builtin_amdgcn_mqsad_pk_u16_u8, "WUiWUiUiWUi", "nc")
BUILTIN(__builtin_amdgcn_mqsad_u32_u8, "V4UiWUiUiV4Ui", "nc")

BUILTIN(__builtin_amdgcn_make_buffer_rsrc, "Qbv*sii", "nc")

//===----------------------------------------------------------------------===//
// Ballot builtins.
//===----------------------------------------------------------------------===//
Expand Down
4 changes: 4 additions & 0 deletions clang/include/clang/Basic/CodeGenOptions.h
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
#ifndef LLVM_CLANG_BASIC_CODEGENOPTIONS_H
#define LLVM_CLANG_BASIC_CODEGENOPTIONS_H

#include "clang/Basic/PointerAuthOptions.h"
#include "clang/Basic/Sanitizers.h"
#include "clang/Basic/XRayInstr.h"
#include "llvm/ADT/FloatingPointMode.h"
Expand Down Expand Up @@ -391,6 +392,9 @@ class CodeGenOptions : public CodeGenOptionsBase {

std::vector<std::string> Reciprocals;

/// Configuration for pointer-signing.
PointerAuthOptions PointerAuth;

/// The preferred width for auto-vectorization transforms. This is intended to
/// override default transforms based on the width of the architected vector
/// registers.
Expand Down
3 changes: 3 additions & 0 deletions clang/include/clang/Basic/DiagnosticCommonKinds.td
Original file line number Diff line number Diff line change
Expand Up @@ -275,6 +275,9 @@ def err_too_large_for_fixed_point : Error<
def err_unimplemented_conversion_with_fixed_point_type : Error<
"conversion between fixed point and %0 is not yet supported">;

def err_requires_positive_value : Error<
"%select{invalid value '%0'; must be positive|value '%0' is too large}1">;

// SEH
def err_seh_expected_handler : Error<
"expected '__except' or '__finally' block">;
Expand Down
12 changes: 12 additions & 0 deletions clang/include/clang/Basic/DiagnosticLexKinds.td
Original file line number Diff line number Diff line change
Expand Up @@ -436,6 +436,14 @@ def warn_cxx23_compat_warning_directive : Warning<
def warn_c23_compat_warning_directive : Warning<
"#warning is incompatible with C standards before C23">,
InGroup<CPre23Compat>, DefaultIgnore;
def ext_pp_embed_directive : ExtWarn<
"#embed is a %select{C23|Clang}0 extension">,
InGroup<C23>;
def warn_compat_pp_embed_directive : Warning<
"#embed is incompatible with C standards before C23">,
InGroup<CPre23Compat>, DefaultIgnore;
def err_pp_embed_dup_params : Error<
"cannot specify parameter '%0' twice in the same '#embed' directive">;

def ext_pp_extra_tokens_at_eol : ExtWarn<
"extra tokens at end of #%0 directive">, InGroup<ExtraTokens>;
Expand Down Expand Up @@ -505,6 +513,8 @@ def err_pp_invalid_directive : Error<
"invalid preprocessing directive%select{|, did you mean '#%1'?}0">;
def warn_pp_invalid_directive : Warning<
err_pp_invalid_directive.Summary>, InGroup<DiagGroup<"unknown-directives">>;
def err_pp_unknown_parameter : Error<
"unknown%select{ | embed}0 preprocessor parameter '%1'">;
def err_pp_directive_required : Error<
"%0 must be used within a preprocessing directive">;
def err_pp_file_not_found : Error<"'%0' file not found">, DefaultFatal;
Expand Down Expand Up @@ -719,6 +729,8 @@ def err_pp_module_build_missing_end : Error<
"no matching '#pragma clang module endbuild' for this '#pragma clang module build'">;

def err_defined_macro_name : Error<"'defined' cannot be used as a macro name">;
def err_defined_in_pp_embed : Error<
"'defined' cannot appear within this context">;
def err_paste_at_start : Error<
"'##' cannot appear at start of macro expansion">;
def err_paste_at_end : Error<"'##' cannot appear at end of macro expansion">;
Expand Down
16 changes: 13 additions & 3 deletions clang/include/clang/Basic/DiagnosticSemaKinds.td
Original file line number Diff line number Diff line change
Expand Up @@ -924,12 +924,21 @@ def err_ptrauth_value_bad_type :
Error<"%select{signed value|extra discriminator|blended pointer|blended "
"integer}0 must have %select{pointer|integer|pointer or integer}1 "
"type; type here is %2">;
def err_ptrauth_bad_constant_pointer :
Error<"argument to ptrauth_sign_constant must refer to a global variable "
"or function">;
def err_ptrauth_bad_constant_discriminator :
Error<"discriminator argument to ptrauth_sign_constant must be a constant "
"integer, the address of the global variable where the result "
"will be stored, or a blend of the two">;
def warn_ptrauth_sign_null_pointer :
Warning<"signing a null pointer will yield a non-null pointer">,
InGroup<PtrAuthNullPointers>;
def warn_ptrauth_auth_null_pointer :
Warning<"authenticating a null pointer will almost certainly trap">,
InGroup<PtrAuthNullPointers>;
def err_ptrauth_string_not_literal : Error<
"argument must be a string literal%select{| of char type}0">;

/// main()
// static main() is not an error in C, just in C++.
Expand Down Expand Up @@ -964,7 +973,7 @@ def err_main_global_variable :
def warn_main_redefined : Warning<"variable named 'main' with external linkage "
"has undefined behavior">, InGroup<Main>;
def ext_main_used : Extension<
"ISO C++ does not allow 'main' to be used by a program">, InGroup<Main>;
"referring to 'main' within an expression is a Clang extension">, InGroup<Main>;

/// parser diagnostics
def ext_no_declarators : ExtWarn<"declaration does not declare anything">,
Expand Down Expand Up @@ -1097,8 +1106,6 @@ def note_surrounding_namespace_starts_here : Note<
"surrounding namespace with visibility attribute starts here">;
def err_pragma_loop_invalid_argument_type : Error<
"invalid argument of type %0; expected an integer type">;
def err_pragma_loop_invalid_argument_value : Error<
"%select{invalid value '%0'; must be positive|value '%0' is too large}1">;
def err_pragma_loop_compatibility : Error<
"%select{incompatible|duplicate}0 directives '%1' and '%2'">;
def err_pragma_loop_precedes_nonloop : Error<
Expand Down Expand Up @@ -12209,6 +12216,9 @@ def err_std_source_location_impl_not_found : Error<
def err_std_source_location_impl_malformed : Error<
"'std::source_location::__impl' must be standard-layout and have only two 'const char *' fields '_M_file_name' and '_M_function_name', and two integral fields '_M_line' and '_M_column'">;

def err_std_initializer_list_malformed : Error<
"%0 layout not recognized. Must be a non-polymorphic class type with no bases and two fields: a 'const E *' and either another 'const E *' or a 'std::size_t'">;

// HLSL Diagnostics
def err_hlsl_attr_unsupported_in_stage : Error<"attribute %0 is unsupported in '%1' shaders, requires %select{|one of the following: }2%3">;
def err_hlsl_attr_invalid_type : Error<
Expand Down
11 changes: 7 additions & 4 deletions clang/include/clang/Basic/FileManager.h
Original file line number Diff line number Diff line change
Expand Up @@ -286,12 +286,15 @@ class FileManager : public RefCountedBase<FileManager> {
/// MemoryBuffer if successful, otherwise returning null.
llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>>
getBufferForFile(FileEntryRef Entry, bool isVolatile = false,
bool RequiresNullTerminator = true);
bool RequiresNullTerminator = true,
std::optional<int64_t> MaybeLimit = std::nullopt);
llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>>
getBufferForFile(StringRef Filename, bool isVolatile = false,
bool RequiresNullTerminator = true) const {
return getBufferForFileImpl(Filename, /*FileSize=*/-1, isVolatile,
RequiresNullTerminator);
bool RequiresNullTerminator = true,
std::optional<int64_t> MaybeLimit = std::nullopt) const {
return getBufferForFileImpl(Filename,
/*FileSize=*/(MaybeLimit ? *MaybeLimit : -1),
isVolatile, RequiresNullTerminator);
}

private:
Expand Down
134 changes: 134 additions & 0 deletions clang/include/clang/Basic/PointerAuthOptions.h
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,144 @@
#ifndef LLVM_CLANG_BASIC_POINTERAUTHOPTIONS_H
#define LLVM_CLANG_BASIC_POINTERAUTHOPTIONS_H

#include "clang/Basic/LLVM.h"
#include "clang/Basic/LangOptions.h"
#include "llvm/ADT/STLForwardCompat.h"
#include "llvm/Support/ErrorHandling.h"
#include "llvm/Target/TargetOptions.h"
#include <optional>

namespace clang {

constexpr unsigned PointerAuthKeyNone = -1;

class PointerAuthSchema {
public:
enum class Kind : unsigned {
None,
ARM8_3,
};

/// Hardware pointer-signing keys in ARM8.3.
///
/// These values are the same used in ptrauth.h.
enum class ARM8_3Key : unsigned {
ASIA = 0,
ASIB = 1,
ASDA = 2,
ASDB = 3
};

/// Forms of extra discrimination.
enum class Discrimination : unsigned {
/// No additional discrimination.
None,

/// Discriminate using a constant value.
Constant,
};

private:
Kind TheKind : 2;
unsigned IsAddressDiscriminated : 1;
unsigned IsIsaPointer : 1;
unsigned AuthenticatesNullValues : 1;
PointerAuthenticationMode SelectedAuthenticationMode : 2;
Discrimination DiscriminationKind : 2;
unsigned Key : 2;
unsigned ConstantDiscriminator : 16;

public:
PointerAuthSchema() : TheKind(Kind::None) {}

PointerAuthSchema(
ARM8_3Key Key, bool IsAddressDiscriminated,
PointerAuthenticationMode AuthenticationMode,
Discrimination OtherDiscrimination,
std::optional<uint16_t> ConstantDiscriminatorOrNone = std::nullopt,
bool IsIsaPointer = false, bool AuthenticatesNullValues = false)
: TheKind(Kind::ARM8_3), IsAddressDiscriminated(IsAddressDiscriminated),
IsIsaPointer(IsIsaPointer),
AuthenticatesNullValues(AuthenticatesNullValues),
SelectedAuthenticationMode(AuthenticationMode),
DiscriminationKind(OtherDiscrimination), Key(llvm::to_underlying(Key)) {
assert((getOtherDiscrimination() != Discrimination::Constant ||
ConstantDiscriminatorOrNone) &&
"constant discrimination requires a constant!");
if (ConstantDiscriminatorOrNone)
ConstantDiscriminator = *ConstantDiscriminatorOrNone;
}

PointerAuthSchema(
ARM8_3Key Key, bool IsAddressDiscriminated,
Discrimination OtherDiscrimination,
std::optional<uint16_t> ConstantDiscriminatorOrNone = std::nullopt,
bool IsIsaPointer = false, bool AuthenticatesNullValues = false)
: PointerAuthSchema(Key, IsAddressDiscriminated,
PointerAuthenticationMode::SignAndAuth,
OtherDiscrimination, ConstantDiscriminatorOrNone,
IsIsaPointer, AuthenticatesNullValues) {}

Kind getKind() const { return TheKind; }

explicit operator bool() const { return isEnabled(); }

bool isEnabled() const { return getKind() != Kind::None; }

bool isAddressDiscriminated() const {
assert(getKind() != Kind::None);
return IsAddressDiscriminated;
}

bool isIsaPointer() const {
assert(getKind() != Kind::None);
return IsIsaPointer;
}

bool authenticatesNullValues() const {
assert(getKind() != Kind::None);
return AuthenticatesNullValues;
}

bool hasOtherDiscrimination() const {
return getOtherDiscrimination() != Discrimination::None;
}

Discrimination getOtherDiscrimination() const {
assert(getKind() != Kind::None);
return DiscriminationKind;
}

uint16_t getConstantDiscrimination() const {
assert(getOtherDiscrimination() == Discrimination::Constant);
return ConstantDiscriminator;
}

unsigned getKey() const {
switch (getKind()) {
case Kind::None:
llvm_unreachable("calling getKey() on disabled schema");
case Kind::ARM8_3:
return llvm::to_underlying(getARM8_3Key());
}
llvm_unreachable("bad key kind");
}

PointerAuthenticationMode getAuthenticationMode() const {
return SelectedAuthenticationMode;
}

ARM8_3Key getARM8_3Key() const {
assert(getKind() == Kind::ARM8_3);
return ARM8_3Key(Key);
}
};

struct PointerAuthOptions {
/// The ABI for C function pointers.
PointerAuthSchema FunctionPointers;
};

} // end namespace clang

#endif
1 change: 1 addition & 0 deletions clang/include/clang/Basic/StmtNodes.td
Original file line number Diff line number Diff line change
Expand Up @@ -204,6 +204,7 @@ def OpaqueValueExpr : StmtNode<Expr>;
def TypoExpr : StmtNode<Expr>;
def RecoveryExpr : StmtNode<Expr>;
def BuiltinBitCastExpr : StmtNode<ExplicitCastExpr>;
def EmbedExpr : StmtNode<Expr>;

// Microsoft Extensions.
def MSPropertyRefExpr : StmtNode<Expr>;
Expand Down
5 changes: 0 additions & 5 deletions clang/include/clang/Basic/TargetInfo.h
Original file line number Diff line number Diff line change
Expand Up @@ -1400,11 +1400,6 @@ class TargetInfo : public TransferrableTargetInfo,
return true;
}

/// For given feature return dependent ones.
virtual StringRef getFeatureDependencies(StringRef Feature) const {
return StringRef();
}

struct BranchProtectionInfo {
LangOptions::SignReturnAddressScopeKind SignReturnAddr;
LangOptions::SignReturnAddressKeyKind SignKey;
Expand Down
6 changes: 6 additions & 0 deletions clang/include/clang/Basic/TokenKinds.def
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,9 @@ PPKEYWORD(error)
// C99 6.10.6 - Pragma Directive.
PPKEYWORD(pragma)

// C23 & C++26 #embed
PPKEYWORD(embed)

// GNU Extensions.
PPKEYWORD(import)
PPKEYWORD(include_next)
Expand Down Expand Up @@ -999,6 +1002,9 @@ ANNOTATION(header_unit)
// Annotation for end of input in clang-repl.
ANNOTATION(repl_input_end)

// Annotation for #embed
ANNOTATION(embed)

#undef PRAGMA_ANNOTATION
#undef ANNOTATION
#undef TESTING_KEYWORD
Expand Down
152 changes: 89 additions & 63 deletions clang/include/clang/Basic/arm_sve.td

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion clang/include/clang/Basic/arm_sve_sme_incl.td
Original file line number Diff line number Diff line change
Expand Up @@ -272,7 +272,7 @@ class Inst<string n, string p, string t, MergeType mt, string i,
string Name = n;
string Prototype = p;
string Types = t;
string TargetGuard = "sve";
string TargetGuard = "sve|sme";
int Merge = mt.Value;
string MergeSuffix = mt.Suffix;
string LLVMIntrinsic = i;
Expand Down
6 changes: 6 additions & 0 deletions clang/include/clang/Driver/Options.td
Original file line number Diff line number Diff line change
Expand Up @@ -884,6 +884,9 @@ will be ignored}]>;
def L : JoinedOrSeparate<["-"], "L">, Flags<[RenderJoined]>, Group<Link_Group>,
Visibility<[ClangOption, FlangOption]>,
MetaVarName<"<dir>">, HelpText<"Add directory to library search path">;
def embed_dir_EQ : Joined<["--"], "embed-dir=">, Group<Preprocessor_Group>,
Visibility<[ClangOption, CC1Option]>, MetaVarName<"<dir>">,
HelpText<"Add directory to embed search path">;
def MD : Flag<["-"], "MD">, Group<M_Group>,
HelpText<"Write a depfile containing user and system headers">;
def MMD : Flag<["-"], "MMD">, Group<M_Group>,
Expand Down Expand Up @@ -1477,6 +1480,9 @@ def dD : Flag<["-"], "dD">, Group<d_Group>, Visibility<[ClangOption, CC1Option]>
def dI : Flag<["-"], "dI">, Group<d_Group>, Visibility<[ClangOption, CC1Option]>,
HelpText<"Print include directives in -E mode in addition to normal output">,
MarshallingInfoFlag<PreprocessorOutputOpts<"ShowIncludeDirectives">>;
def dE : Flag<["-"], "dE">, Group<d_Group>, Visibility<[CC1Option]>,
HelpText<"Print embed directives in -E mode in addition to normal output">,
MarshallingInfoFlag<PreprocessorOutputOpts<"ShowEmbedDirectives">>;
def dM : Flag<["-"], "dM">, Group<d_Group>, Visibility<[ClangOption, CC1Option, FlangOption, FC1Option]>,
HelpText<"Print macro definitions in -E mode instead of normal output">;
def dead__strip : Flag<["-"], "dead_strip">;
Expand Down
9 changes: 9 additions & 0 deletions clang/include/clang/Frontend/CompilerInvocation.h
Original file line number Diff line number Diff line change
Expand Up @@ -305,6 +305,15 @@ class CompilerInvocation : public CompilerInvocationBase {
/// executable), for finding the builtin compiler path.
static std::string GetResourcesPath(const char *Argv0, void *MainAddr);

/// Populate \p Opts with the default set of pointer authentication-related
/// options given \p LangOpts and \p Triple.
///
/// Note: This is intended to be used by tools which must be aware of
/// pointer authentication-related code generation, e.g. lldb.
static void setDefaultPointerAuthOptions(PointerAuthOptions &Opts,
const LangOptions &LangOpts,
const llvm::Triple &Triple);

/// Retrieve a module hash string that is suitable for uniquely
/// identifying the conditions under which the module was built.
std::string getModuleHash() const;
Expand Down
3 changes: 3 additions & 0 deletions clang/include/clang/Frontend/PreprocessorOutputOptions.h
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,8 @@ class PreprocessorOutputOptions {
LLVM_PREFERRED_TYPE(bool)
unsigned ShowIncludeDirectives : 1; ///< Print includes, imports etc. within preprocessed output.
LLVM_PREFERRED_TYPE(bool)
unsigned ShowEmbedDirectives : 1; ///< Print embeds, etc. within preprocessed
LLVM_PREFERRED_TYPE(bool)
unsigned RewriteIncludes : 1; ///< Preprocess include directives only.
LLVM_PREFERRED_TYPE(bool)
unsigned RewriteImports : 1; ///< Include contents of transitively-imported modules.
Expand All @@ -51,6 +53,7 @@ class PreprocessorOutputOptions {
ShowMacroComments = 0;
ShowMacros = 0;
ShowIncludeDirectives = 0;
ShowEmbedDirectives = 0;
RewriteIncludes = 0;
RewriteImports = 0;
MinimizeWhitespace = 0;
Expand Down
57 changes: 56 additions & 1 deletion clang/include/clang/Lex/ExternalPreprocessorSource.h
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,9 @@
#ifndef LLVM_CLANG_LEX_EXTERNALPREPROCESSORSOURCE_H
#define LLVM_CLANG_LEX_EXTERNALPREPROCESSORSOURCE_H

#include <cassert>
#include <cstdint>

namespace clang {

class IdentifierInfo;
Expand All @@ -36,12 +39,64 @@ class ExternalPreprocessorSource {
/// Return the identifier associated with the given ID number.
///
/// The ID 0 is associated with the NULL identifier.
virtual IdentifierInfo *GetIdentifier(unsigned ID) = 0;
virtual IdentifierInfo *GetIdentifier(uint64_t ID) = 0;

/// Map a module ID to a module.
virtual Module *getModule(unsigned ModuleID) = 0;
};

// Either a pointer to an IdentifierInfo of the controlling macro or the ID
// number of the controlling macro.
class LazyIdentifierInfoPtr {
// If the low bit is clear, a pointer to the IdentifierInfo. If the low
// bit is set, the upper 63 bits are the ID number.
mutable uint64_t Ptr = 0;

public:
LazyIdentifierInfoPtr() = default;

explicit LazyIdentifierInfoPtr(const IdentifierInfo *Ptr)
: Ptr(reinterpret_cast<uint64_t>(Ptr)) {}

explicit LazyIdentifierInfoPtr(uint64_t ID) : Ptr((ID << 1) | 0x01) {
assert((ID << 1 >> 1) == ID && "ID must require < 63 bits");
if (ID == 0)
Ptr = 0;
}

LazyIdentifierInfoPtr &operator=(const IdentifierInfo *Ptr) {
this->Ptr = reinterpret_cast<uint64_t>(Ptr);
return *this;
}

LazyIdentifierInfoPtr &operator=(uint64_t ID) {
assert((ID << 1 >> 1) == ID && "IDs must require < 63 bits");
if (ID == 0)
Ptr = 0;
else
Ptr = (ID << 1) | 0x01;

return *this;
}

/// Whether this pointer is non-NULL.
///
/// This operation does not require the AST node to be deserialized.
bool isValid() const { return Ptr != 0; }

/// Whether this pointer is currently stored as ID.
bool isID() const { return Ptr & 0x01; }

IdentifierInfo *getPtr() const {
assert(!isID());
return reinterpret_cast<IdentifierInfo *>(Ptr);
}

uint64_t getID() const {
assert(isID());
return Ptr >> 1;
}
};
}

#endif
12 changes: 3 additions & 9 deletions clang/include/clang/Lex/HeaderSearch.h
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
#include "clang/Basic/SourceLocation.h"
#include "clang/Basic/SourceManager.h"
#include "clang/Lex/DirectoryLookup.h"
#include "clang/Lex/ExternalPreprocessorSource.h"
#include "clang/Lex/HeaderMap.h"
#include "clang/Lex/ModuleMap.h"
#include "llvm/ADT/ArrayRef.h"
Expand Down Expand Up @@ -121,13 +122,6 @@ struct HeaderFileInfo {
LLVM_PREFERRED_TYPE(bool)
unsigned IsValid : 1;

/// The ID number of the controlling macro.
///
/// This ID number will be non-zero when there is a controlling
/// macro whose IdentifierInfo may not yet have been loaded from
/// external storage.
unsigned ControllingMacroID = 0;

/// If this file has a \#ifndef XXX (or equivalent) guard that
/// protects the entire contents of the file, this is the identifier
/// for the macro that controls whether or not it has any effect.
Expand All @@ -136,7 +130,7 @@ struct HeaderFileInfo {
/// the controlling macro of this header, since
/// getControllingMacro() is able to load a controlling macro from
/// external storage.
const IdentifierInfo *ControllingMacro = nullptr;
LazyIdentifierInfoPtr LazyControllingMacro;

/// If this header came from a framework include, this is the name
/// of the framework.
Expand Down Expand Up @@ -582,7 +576,7 @@ class HeaderSearch {
/// no-op \#includes.
void SetFileControllingMacro(FileEntryRef File,
const IdentifierInfo *ControllingMacro) {
getFileInfo(File).ControllingMacro = ControllingMacro;
getFileInfo(File).LazyControllingMacro = ControllingMacro;
}

/// Determine whether this file is intended to be safe from
Expand Down
10 changes: 0 additions & 10 deletions clang/include/clang/Lex/HeaderSearchOptions.h
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@

#include "clang/Basic/LLVM.h"
#include "llvm/ADT/CachedHashString.h"
#include "llvm/ADT/Hashing.h"
#include "llvm/ADT/SetVector.h"
#include "llvm/ADT/StringRef.h"
#include "llvm/Support/HashBuilder.h"
Expand Down Expand Up @@ -310,21 +309,12 @@ class HeaderSearchOptions {
}
};

inline llvm::hash_code hash_value(const HeaderSearchOptions::Entry &E) {
return llvm::hash_combine(E.Path, E.Group, E.IsFramework, E.IgnoreSysRoot);
}

template <typename HasherT, llvm::endianness Endianness>
inline void addHash(llvm::HashBuilder<HasherT, Endianness> &HBuilder,
const HeaderSearchOptions::Entry &E) {
HBuilder.add(E.Path, E.Group, E.IsFramework, E.IgnoreSysRoot);
}

inline llvm::hash_code
hash_value(const HeaderSearchOptions::SystemHeaderPrefix &SHP) {
return llvm::hash_combine(SHP.Prefix, SHP.IsSystemHeader);
}

template <typename HasherT, llvm::endianness Endianness>
inline void addHash(llvm::HashBuilder<HasherT, Endianness> &HBuilder,
const HeaderSearchOptions::SystemHeaderPrefix &SHP) {
Expand Down
54 changes: 54 additions & 0 deletions clang/include/clang/Lex/PPCallbacks.h
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ class IdentifierInfo;
class MacroDefinition;
class MacroDirective;
class MacroArgs;
struct LexEmbedParametersResult;

/// This interface provides a way to observe the actions of the
/// preprocessor as it does its thing.
Expand Down Expand Up @@ -83,6 +84,34 @@ class PPCallbacks {
const Token &FilenameTok,
SrcMgr::CharacteristicKind FileType) {}

/// Callback invoked whenever the preprocessor cannot find a file for an
/// embed directive.
///
/// \param FileName The name of the file being included, as written in the
/// source code.
///
/// \returns true to indicate that the preprocessor should skip this file
/// and not issue any diagnostic.
virtual bool EmbedFileNotFound(StringRef FileName) { return false; }

/// Callback invoked whenever an embed directive has been processed,
/// regardless of whether the embed will actually find a file.
///
/// \param HashLoc The location of the '#' that starts the embed directive.
///
/// \param FileName The name of the file being included, as written in the
/// source code.
///
/// \param IsAngled Whether the file name was enclosed in angle brackets;
/// otherwise, it was enclosed in quotes.
///
/// \param File The actual file that may be included by this embed directive.
///
/// \param Params The parameters used by the directive.
virtual void EmbedDirective(SourceLocation HashLoc, StringRef FileName,
bool IsAngled, OptionalFileEntryRef File,
const LexEmbedParametersResult &Params) {}

/// Callback invoked whenever the preprocessor cannot find a file for an
/// inclusion directive.
///
Expand Down Expand Up @@ -333,6 +362,10 @@ class PPCallbacks {
SourceRange Range) {
}

/// Hook called when a '__has_embed' directive is read.
virtual void HasEmbed(SourceLocation Loc, StringRef FileName, bool IsAngled,
OptionalFileEntryRef File) {}

/// Hook called when a '__has_include' or '__has_include_next' directive is
/// read.
virtual void HasInclude(SourceLocation Loc, StringRef FileName, bool IsAngled,
Expand Down Expand Up @@ -464,6 +497,21 @@ class PPChainedCallbacks : public PPCallbacks {
Second->FileSkipped(SkippedFile, FilenameTok, FileType);
}

bool EmbedFileNotFound(StringRef FileName) override {
bool Skip = First->FileNotFound(FileName);
// Make sure to invoke the second callback, no matter if the first already
// returned true to skip the file.
Skip |= Second->FileNotFound(FileName);
return Skip;
}

void EmbedDirective(SourceLocation HashLoc, StringRef FileName, bool IsAngled,
OptionalFileEntryRef File,
const LexEmbedParametersResult &Params) override {
First->EmbedDirective(HashLoc, FileName, IsAngled, File, Params);
Second->EmbedDirective(HashLoc, FileName, IsAngled, File, Params);
}

bool FileNotFound(StringRef FileName) override {
bool Skip = First->FileNotFound(FileName);
// Make sure to invoke the second callback, no matter if the first already
Expand Down Expand Up @@ -565,6 +613,12 @@ class PPChainedCallbacks : public PPCallbacks {
Second->PragmaDiagnostic(Loc, Namespace, mapping, Str);
}

void HasEmbed(SourceLocation Loc, StringRef FileName, bool IsAngled,
OptionalFileEntryRef File) override {
First->HasEmbed(Loc, FileName, IsAngled, File);
Second->HasEmbed(Loc, FileName, IsAngled, File);
}

void HasInclude(SourceLocation Loc, StringRef FileName, bool IsAngled,
OptionalFileEntryRef File,
SrcMgr::CharacteristicKind FileType) override;
Expand Down
33 changes: 33 additions & 0 deletions clang/include/clang/Lex/PPDirectiveParameter.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
//===--- PPDirectiveParameter.h ---------------------------------*- C++ -*-===//
//
// 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 defines the base class for preprocessor directive parameters, such
// as limit(1) or suffix(x) for #embed.
//
//===----------------------------------------------------------------------===//

#ifndef LLVM_CLANG_LEX_PPDIRECTIVEPARAMETER_H
#define LLVM_CLANG_LEX_PPDIRECTIVEPARAMETER_H

#include "clang/Basic/SourceLocation.h"

namespace clang {

/// Captures basic information about a preprocessor directive parameter.
class PPDirectiveParameter {
SourceRange R;

public:
PPDirectiveParameter(SourceRange R) : R(R) {}

SourceRange getParameterRange() const { return R; }
};

} // end namespace clang

#endif
94 changes: 94 additions & 0 deletions clang/include/clang/Lex/PPEmbedParameters.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
//===--- PPEmbedParameters.h ------------------------------------*- C++ -*-===//
//
// 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
//
//===----------------------------------------------------------------------===//
//
// Defines all of the preprocessor directive parmeters for #embed
//
//===----------------------------------------------------------------------===//

#ifndef LLVM_CLANG_LEX_PPEMBEDPARAMETERS_H
#define LLVM_CLANG_LEX_PPEMBEDPARAMETERS_H

#include "clang/Lex/PPDirectiveParameter.h"
#include "clang/Lex/Token.h"
#include "llvm/ADT/SmallVector.h"

namespace clang {

/// Preprocessor extension embed parameter "clang::offset"
/// `clang::offset( constant-expression )`
class PPEmbedParameterOffset : public PPDirectiveParameter {
public:
size_t Offset;

PPEmbedParameterOffset(size_t Offset, SourceRange R)
: PPDirectiveParameter(R), Offset(Offset) {}
};

/// Preprocessor standard embed parameter "limit"
/// `limit( constant-expression )`
class PPEmbedParameterLimit : public PPDirectiveParameter {
public:
size_t Limit;

PPEmbedParameterLimit(size_t Limit, SourceRange R)
: PPDirectiveParameter(R), Limit(Limit) {}
};

/// Preprocessor standard embed parameter "prefix"
/// `prefix( balanced-token-seq )`
class PPEmbedParameterPrefix : public PPDirectiveParameter {
public:
SmallVector<Token, 2> Tokens;

PPEmbedParameterPrefix(SmallVectorImpl<Token> &&Tokens, SourceRange R)
: PPDirectiveParameter(R), Tokens(std::move(Tokens)) {}
};

/// Preprocessor standard embed parameter "suffix"
/// `suffix( balanced-token-seq )`
class PPEmbedParameterSuffix : public PPDirectiveParameter {
public:
SmallVector<Token, 2> Tokens;

PPEmbedParameterSuffix(SmallVectorImpl<Token> &&Tokens, SourceRange R)
: PPDirectiveParameter(R), Tokens(std::move(Tokens)) {}
};

/// Preprocessor standard embed parameter "if_empty"
/// `if_empty( balanced-token-seq )`
class PPEmbedParameterIfEmpty : public PPDirectiveParameter {
public:
SmallVector<Token, 2> Tokens;

PPEmbedParameterIfEmpty(SmallVectorImpl<Token> &&Tokens, SourceRange R)
: PPDirectiveParameter(R), Tokens(std::move(Tokens)) {}
};

struct LexEmbedParametersResult {
std::optional<PPEmbedParameterLimit> MaybeLimitParam;
std::optional<PPEmbedParameterOffset> MaybeOffsetParam;
std::optional<PPEmbedParameterIfEmpty> MaybeIfEmptyParam;
std::optional<PPEmbedParameterPrefix> MaybePrefixParam;
std::optional<PPEmbedParameterSuffix> MaybeSuffixParam;
SourceRange ParamRange;
int UnrecognizedParams;

size_t PrefixTokenCount() const {
if (MaybePrefixParam)
return MaybePrefixParam->Tokens.size();
return 0;
}
size_t SuffixTokenCount() const {
if (MaybeSuffixParam)
return MaybeSuffixParam->Tokens.size();
return 0;
}
};
} // end namespace clang

#endif
69 changes: 67 additions & 2 deletions clang/include/clang/Lex/Preprocessor.h
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,10 @@
#include "clang/Lex/ModuleLoader.h"
#include "clang/Lex/ModuleMap.h"
#include "clang/Lex/PPCallbacks.h"
#include "clang/Lex/PPEmbedParameters.h"
#include "clang/Lex/Token.h"
#include "clang/Lex/TokenLexer.h"
#include "llvm/ADT/APSInt.h"
#include "llvm/ADT/ArrayRef.h"
#include "llvm/ADT/DenseMap.h"
#include "llvm/ADT/FoldingSet.h"
Expand Down Expand Up @@ -119,6 +121,13 @@ enum MacroUse {
MU_Undef = 2
};

enum class EmbedResult {
Invalid = -1, // Parsing error occurred.
NotFound = 0, // Corresponds to __STDC_EMBED_NOT_FOUND__
Found = 1, // Corresponds to __STDC_EMBED_FOUND__
Empty = 2, // Corresponds to __STDC_EMBED_EMPTY__
};

/// Engages in a tight little dance with the lexer to efficiently
/// preprocess tokens.
///
Expand Down Expand Up @@ -165,6 +174,7 @@ class Preprocessor {
IdentifierInfo *Ident__has_builtin; // __has_builtin
IdentifierInfo *Ident__has_constexpr_builtin; // __has_constexpr_builtin
IdentifierInfo *Ident__has_attribute; // __has_attribute
IdentifierInfo *Ident__has_embed; // __has_embed
IdentifierInfo *Ident__has_include; // __has_include
IdentifierInfo *Ident__has_include_next; // __has_include_next
IdentifierInfo *Ident__has_warning; // __has_warning
Expand Down Expand Up @@ -1734,6 +1744,10 @@ class Preprocessor {
/// Lex a token, forming a header-name token if possible.
bool LexHeaderName(Token &Result, bool AllowMacroExpansion = true);

/// Lex the parameters for an #embed directive, returns nullopt on error.
std::optional<LexEmbedParametersResult> LexEmbedParameters(Token &Current,
bool ForHasEmbed);

bool LexAfterModuleImport(Token &Result);
void CollectPpImportSuffix(SmallVectorImpl<Token> &Toks);

Expand Down Expand Up @@ -2314,7 +2328,13 @@ class Preprocessor {

/// Read and discard all tokens remaining on the current line until
/// the tok::eod token is found. Returns the range of the skipped tokens.
SourceRange DiscardUntilEndOfDirective();
SourceRange DiscardUntilEndOfDirective() {
Token Tmp;
return DiscardUntilEndOfDirective(Tmp);
}

/// Same as above except retains the token that was found.
SourceRange DiscardUntilEndOfDirective(Token &Tok);

/// Returns true if the preprocessor has seen a use of
/// __DATE__ or __TIME__ in the file so far.
Expand Down Expand Up @@ -2419,6 +2439,18 @@ class Preprocessor {
bool *IsFrameworkFound, bool SkipCache = false,
bool OpenFile = true, bool CacheFailures = true);

/// Given a "Filename" or \<Filename> reference, look up the indicated embed
/// resource. \p isAngled indicates whether the file reference is for
/// system \#include's or not (i.e. using <> instead of ""). If \p OpenFile
/// is true, the file looked up is opened for reading, otherwise it only
/// validates that the file exists. Quoted filenames are looked up relative
/// to \p LookupFromFile if it is nonnull.
///
/// Returns std::nullopt on failure.
OptionalFileEntryRef
LookupEmbedFile(StringRef Filename, bool isAngled, bool OpenFile,
const FileEntry *LookupFromFile = nullptr);

/// Return true if we're in the top-level file, not in a \#include.
bool isInPrimaryFile() const;

Expand Down Expand Up @@ -2524,6 +2556,9 @@ class Preprocessor {
/// Information about the result for evaluating an expression for a
/// preprocessor directive.
struct DirectiveEvalResult {
/// The integral value of the expression.
std::optional<llvm::APSInt> Value;

/// Whether the expression was evaluated as true or not.
bool Conditional;

Expand All @@ -2538,7 +2573,25 @@ class Preprocessor {
/// \#if or \#elif directive and return a \p DirectiveEvalResult object.
///
/// If the expression is equivalent to "!defined(X)" return X in IfNDefMacro.
DirectiveEvalResult EvaluateDirectiveExpression(IdentifierInfo *&IfNDefMacro);
DirectiveEvalResult EvaluateDirectiveExpression(IdentifierInfo *&IfNDefMacro,
bool CheckForEoD = true);

/// Evaluate an integer constant expression that may occur after a
/// \#if or \#elif directive and return a \p DirectiveEvalResult object.
///
/// If the expression is equivalent to "!defined(X)" return X in IfNDefMacro.
/// \p EvaluatedDefined will contain the result of whether "defined" appeared
/// in the evaluated expression or not.
DirectiveEvalResult EvaluateDirectiveExpression(IdentifierInfo *&IfNDefMacro,
Token &Tok,
bool &EvaluatedDefined,
bool CheckForEoD = true);

/// Process a '__has_embed("path" [, ...])' expression.
///
/// Returns predefined `__STDC_EMBED_*` macro values if
/// successful.
EmbedResult EvaluateHasEmbed(Token &Tok, IdentifierInfo *II);

/// Process a '__has_include("path")' expression.
///
Expand Down Expand Up @@ -2686,6 +2739,12 @@ class Preprocessor {
const FileEntry *LookupFromFile, StringRef &LookupFilename,
SmallVectorImpl<char> &RelativePath, SmallVectorImpl<char> &SearchPath,
ModuleMap::KnownHeader &SuggestedModule, bool isAngled);
// Binary data inclusion
void HandleEmbedDirective(SourceLocation HashLoc, Token &Tok,
const FileEntry *LookupFromFile = nullptr);
void HandleEmbedDirectiveImpl(SourceLocation HashLoc,
const LexEmbedParametersResult &Params,
StringRef BinaryContents);

// File inclusion.
void HandleIncludeDirective(SourceLocation HashLoc, Token &Tok,
Expand Down Expand Up @@ -3002,6 +3061,12 @@ class EmptylineHandler {
virtual void HandleEmptyline(SourceRange Range) = 0;
};

/// Helper class to shuttle information about #embed directives from the
/// preprocessor to the parser through an annotation token.
struct EmbedAnnotationData {
StringRef BinaryData;
};

/// Registry of pragma handlers added by plugins
using PragmaHandlerRegistry = llvm::Registry<PragmaHandler>;

Expand Down
3 changes: 3 additions & 0 deletions clang/include/clang/Lex/PreprocessorOptions.h
Original file line number Diff line number Diff line change
Expand Up @@ -170,6 +170,9 @@ class PreprocessorOptions {
/// of the specified memory buffer (the second part of each pair).
std::vector<std::pair<std::string, llvm::MemoryBuffer *>> RemappedFileBuffers;

/// User specified embed entries.
std::vector<std::string> EmbedEntries;

/// Whether the compiler instance should retain (i.e., not free)
/// the buffers associated with remapped files.
///
Expand Down
3 changes: 3 additions & 0 deletions clang/include/clang/Parse/Parser.h
Original file line number Diff line number Diff line change
Expand Up @@ -2122,6 +2122,8 @@ class Parser : public CodeCompletionHandler {
QualType PreferredBaseType;
};
ExprResult ParseInitializerWithPotentialDesignator(DesignatorCompletionInfo);
ExprResult createEmbedExpr();
void ExpandEmbedDirective(SmallVectorImpl<Expr *> &Exprs);

//===--------------------------------------------------------------------===//
// clang Expressions
Expand Down Expand Up @@ -3813,6 +3815,7 @@ class Parser : public CodeCompletionHandler {
AnnotateTemplateIdTokenAsType(CXXScopeSpec &SS,
ImplicitTypenameContext AllowImplicitTypename,
bool IsClassName = false);
void ExpandEmbedIntoTemplateArgList(TemplateArgList &TemplateArgs);
bool ParseTemplateArgumentList(TemplateArgList &TemplateArgs,
TemplateTy Template, SourceLocation OpenLoc);
ParsedTemplateArgument ParseTemplateTemplateArgument();
Expand Down
8 changes: 6 additions & 2 deletions clang/include/clang/Sema/Sema.h
Original file line number Diff line number Diff line change
Expand Up @@ -4031,8 +4031,8 @@ class Sema final : public SemaBase {
const ParsedAttributesView &AttrList);
Decl *ActOnUsingEnumDeclaration(Scope *CurScope, AccessSpecifier AS,
SourceLocation UsingLoc,
SourceLocation EnumLoc,
SourceLocation IdentLoc, IdentifierInfo &II,
SourceLocation EnumLoc, SourceRange TyLoc,
const IdentifierInfo &II, ParsedType Ty,
CXXScopeSpec *SS = nullptr);
Decl *ActOnAliasDeclaration(Scope *CurScope, AccessSpecifier AS,
MultiTemplateParamsArg TemplateParams,
Expand Down Expand Up @@ -5735,6 +5735,10 @@ class Sema final : public SemaBase {
SourceLocation BuiltinLoc,
SourceLocation RPLoc);

// #embed
ExprResult ActOnEmbedExpr(SourceLocation EmbedKeywordLoc,
StringLiteral *BinaryData);

// Build a potentially resolved SourceLocExpr.
ExprResult BuildSourceLocExpr(SourceLocIdentKind Kind, QualType ResultTy,
SourceLocation BuiltinLoc, SourceLocation RPLoc,
Expand Down
57 changes: 42 additions & 15 deletions clang/include/clang/Serialization/ASTBitCodes.h
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
#include "clang/Serialization/SourceLocationEncoding.h"
#include "llvm/ADT/DenseMapInfo.h"
#include "llvm/Bitstream/BitCodes.h"
#include "llvm/Support/MathExtras.h"
#include <cassert>
#include <cstdint>

Expand Down Expand Up @@ -59,7 +60,7 @@ const unsigned VERSION_MINOR = 1;
///
/// The ID numbers of identifiers are consecutive (in order of discovery)
/// and start at 1. 0 is reserved for NULL.
using IdentifierID = uint32_t;
using IdentifierID = uint64_t;

/// The number of predefined identifier IDs.
const unsigned int NUM_PREDEF_IDENT_IDS = 1;
Expand All @@ -70,41 +71,64 @@ using DeclID = DeclIDBase::DeclID;

/// An ID number that refers to a type in an AST file.
///
/// The ID of a type is partitioned into two parts: the lower
/// three bits are used to store the const/volatile/restrict
/// qualifiers (as with QualType) and the upper bits provide a
/// type index. The type index values are partitioned into two
/// The ID of a type is partitioned into three parts:
/// - the lower three bits are used to store the const/volatile/restrict
/// qualifiers (as with QualType).
/// - the next 29 bits provide a type index in the corresponding
/// module file.
/// - the upper 32 bits provide a module file index.
///
/// The type index values are partitioned into two
/// sets. The values below NUM_PREDEF_TYPE_IDs are predefined type
/// IDs (based on the PREDEF_TYPE_*_ID constants), with 0 as a
/// placeholder for "no type". Values from NUM_PREDEF_TYPE_IDs are
/// other types that have serialized representations.
using TypeID = uint32_t;
/// placeholder for "no type". The module file index for predefined
/// types are always 0 since they don't belong to any modules.
/// Values from NUM_PREDEF_TYPE_IDs are other types that have
/// serialized representations.
using TypeID = uint64_t;
/// Same with TypeID except that the LocalTypeID is only meaningful
/// with the corresponding ModuleFile.
///
/// FIXME: Make TypeID and LocalTypeID a class to improve the type
/// safety.
using LocalTypeID = TypeID;

/// A type index; the type ID with the qualifier bits removed.
/// Keep structure alignment 32-bit since the blob is assumed as 32-bit
/// aligned.
class TypeIdx {
uint32_t ModuleFileIndex = 0;
uint32_t Idx = 0;

public:
TypeIdx() = default;
explicit TypeIdx(uint32_t index) : Idx(index) {}

uint32_t getIndex() const { return Idx; }
explicit TypeIdx(uint32_t ModuleFileIdx, uint32_t Idx)
: ModuleFileIndex(ModuleFileIdx), Idx(Idx) {}

uint32_t getModuleFileIndex() const { return ModuleFileIndex; }

uint64_t getValue() const { return ((uint64_t)ModuleFileIndex << 32) | Idx; }

TypeID asTypeID(unsigned FastQuals) const {
if (Idx == uint32_t(-1))
return TypeID(-1);

return (Idx << Qualifiers::FastWidth) | FastQuals;
unsigned Index = (Idx << Qualifiers::FastWidth) | FastQuals;
return ((uint64_t)ModuleFileIndex << 32) | Index;
}

static TypeIdx fromTypeID(TypeID ID) {
if (ID == TypeID(-1))
return TypeIdx(-1);
return TypeIdx(0, -1);

return TypeIdx(ID >> Qualifiers::FastWidth);
return TypeIdx(ID >> 32, (ID & llvm::maskTrailingOnes<TypeID>(32)) >>
Qualifiers::FastWidth);
}
};

static_assert(alignof(TypeIdx) == 4);

/// A structure for putting "fast"-unqualified QualTypes into a
/// DenseMap. This uses the standard pointer hash function.
struct UnsafeQualTypeDenseMapInfo {
Expand Down Expand Up @@ -1658,6 +1682,9 @@ enum StmtCode {
/// A SourceLocExpr record.
EXPR_SOURCE_LOC,

/// A EmbedExpr record.
EXPR_BUILTIN_PP_EMBED,

/// A ShuffleVectorExpr record.
EXPR_SHUFFLE_VECTOR,

Expand Down Expand Up @@ -2004,9 +2031,9 @@ struct ObjCCategoriesInfo {

ObjCCategoriesInfo() = default;
ObjCCategoriesInfo(LocalDeclID ID, unsigned Offset)
: DefinitionID(ID.get()), Offset(Offset) {}
: DefinitionID(ID.getRawValue()), Offset(Offset) {}

LocalDeclID getDefinitionID() const { return LocalDeclID(DefinitionID); }
DeclID getDefinitionID() const { return DefinitionID; }

friend bool operator<(const ObjCCategoriesInfo &X,
const ObjCCategoriesInfo &Y) {
Expand Down
53 changes: 25 additions & 28 deletions clang/include/clang/Serialization/ASTReader.h
Original file line number Diff line number Diff line change
Expand Up @@ -375,6 +375,7 @@ class ASTReader
friend class serialization::reader::ASTIdentifierLookupTrait;
friend class serialization::ReadMethodPoolVisitor;
friend class TypeLocReader;
friend class LocalDeclID;

using RecordData = SmallVector<uint64_t, 64>;
using RecordDataImpl = SmallVectorImpl<uint64_t>;
Expand Down Expand Up @@ -490,14 +491,6 @@ class ASTReader
/// ID = (I + 1) << FastQual::Width has already been loaded
llvm::PagedVector<QualType> TypesLoaded;

using GlobalTypeMapType =
ContinuousRangeMap<serialization::TypeID, ModuleFile *, 4>;

/// Mapping from global type IDs to the module in which the
/// type resides along with the offset that should be added to the
/// global type ID to produce a local ID.
GlobalTypeMapType GlobalTypeMap;

/// Declarations that have already been loaded from the chain.
///
/// When the pointer at index I is non-NULL, the declaration with ID
Expand Down Expand Up @@ -660,14 +653,6 @@ class ASTReader
/// been loaded.
std::vector<IdentifierInfo *> IdentifiersLoaded;

using GlobalIdentifierMapType =
ContinuousRangeMap<serialization::IdentifierID, ModuleFile *, 4>;

/// Mapping from global identifier IDs to the module in which the
/// identifier resides along with the offset that should be added to the
/// global identifier ID to produce a local ID.
GlobalIdentifierMapType GlobalIdentifierMap;

/// A vector containing macros that have already been
/// loaded.
///
Expand Down Expand Up @@ -1436,8 +1421,8 @@ class ASTReader
RecordLocation(ModuleFile *M, uint64_t O) : F(M), Offset(O) {}
};

QualType readTypeRecord(unsigned Index);
RecordLocation TypeCursorForIndex(unsigned Index);
QualType readTypeRecord(serialization::TypeID ID);
RecordLocation TypeCursorForIndex(serialization::TypeID ID);
void LoadedDecl(unsigned Index, Decl *D);
Decl *ReadDeclRecord(GlobalDeclID ID);
void markIncompleteDeclChain(Decl *D);
Expand Down Expand Up @@ -1501,7 +1486,8 @@ class ASTReader
: iterator_adaptor_base(Pos), Reader(Reader), Mod(Mod) {}

value_type operator*() const {
return Reader->GetDecl(Reader->getGlobalDeclID(*Mod, (LocalDeclID)*I));
LocalDeclID ID = LocalDeclID::get(*Reader, *Mod, *I);
return Reader->GetDecl(Reader->getGlobalDeclID(*Mod, ID));
}

value_type operator->() const { return **this; }
Expand Down Expand Up @@ -1545,6 +1531,16 @@ class ASTReader
/// Translate a \param GlobalDeclID to the index of DeclsLoaded array.
unsigned translateGlobalDeclIDToIndex(GlobalDeclID ID) const;

/// Translate an \param IdentifierID ID to the index of IdentifiersLoaded
/// array and the corresponding module file.
std::pair<ModuleFile *, unsigned>
translateIdentifierIDToIndex(serialization::IdentifierID ID) const;

/// Translate an \param TypeID ID to the index of TypesLoaded
/// array and the corresponding module file.
std::pair<ModuleFile *, unsigned>
translateTypeIDToIndex(serialization::TypeID ID) const;

public:
/// Load the AST file and validate its contents against the given
/// Preprocessor.
Expand Down Expand Up @@ -1893,10 +1889,11 @@ class ASTReader
QualType GetType(serialization::TypeID ID);

/// Resolve a local type ID within a given AST file into a type.
QualType getLocalType(ModuleFile &F, unsigned LocalID);
QualType getLocalType(ModuleFile &F, serialization::LocalTypeID LocalID);

/// Map a local type ID within a given AST file into a global type ID.
serialization::TypeID getGlobalTypeID(ModuleFile &F, unsigned LocalID) const;
serialization::TypeID
getGlobalTypeID(ModuleFile &F, serialization::LocalTypeID LocalID) const;

/// Read a type from the current position in the given record, which
/// was read from the given AST file.
Expand Down Expand Up @@ -1955,12 +1952,12 @@ class ASTReader
/// given module.
///
/// \returns The declaration ID read from the record, adjusted to a global ID.
GlobalDeclID ReadDeclID(ModuleFile &F, const RecordData &Record,
GlobalDeclID ReadDeclID(ModuleFile &F, const RecordDataImpl &Record,
unsigned &Idx);

/// Reads a declaration from the given position in a record in the
/// given module.
Decl *ReadDecl(ModuleFile &F, const RecordData &R, unsigned &I) {
Decl *ReadDecl(ModuleFile &F, const RecordDataImpl &R, unsigned &I) {
return GetDecl(ReadDeclID(F, R, I));
}

Expand All @@ -1969,8 +1966,8 @@ class ASTReader
///
/// \returns The declaration read from this location, casted to the given
/// result type.
template<typename T>
T *ReadDeclAs(ModuleFile &F, const RecordData &R, unsigned &I) {
template <typename T>
T *ReadDeclAs(ModuleFile &F, const RecordDataImpl &R, unsigned &I) {
return cast_or_null<T>(GetDecl(ReadDeclID(F, R, I)));
}

Expand Down Expand Up @@ -2129,7 +2126,7 @@ class ASTReader
/// Load a selector from disk, registering its ID if it exists.
void LoadSelector(Selector Sel);

void SetIdentifierInfo(unsigned ID, IdentifierInfo *II);
void SetIdentifierInfo(serialization::IdentifierID ID, IdentifierInfo *II);
void SetGloballyVisibleDecls(IdentifierInfo *II,
const SmallVectorImpl<GlobalDeclID> &DeclIDs,
SmallVectorImpl<Decl *> *Decls = nullptr);
Expand All @@ -2156,10 +2153,10 @@ class ASTReader
return DecodeIdentifierInfo(ID);
}

IdentifierInfo *getLocalIdentifier(ModuleFile &M, unsigned LocalID);
IdentifierInfo *getLocalIdentifier(ModuleFile &M, uint64_t LocalID);

serialization::IdentifierID getGlobalIdentifierID(ModuleFile &M,
unsigned LocalID);
uint64_t LocalID);

void resolvePendingMacro(IdentifierInfo *II, const PendingMacroInfo &PMInfo);

Expand Down
2 changes: 1 addition & 1 deletion clang/include/clang/Serialization/ASTRecordReader.h
Original file line number Diff line number Diff line change
Expand Up @@ -163,7 +163,7 @@ class ASTRecordReader
void readTypeLoc(TypeLoc TL, LocSeq *Seq = nullptr);

/// Map a local type ID within a given AST file to a global type ID.
serialization::TypeID getGlobalTypeID(unsigned LocalID) const {
serialization::TypeID getGlobalTypeID(serialization::TypeID LocalID) const {
return Reader->getGlobalTypeID(*F, LocalID);
}

Expand Down
Loading