diff --git a/include/cpp2reflect.h b/include/cpp2reflect.h new file mode 100644 index 000000000..b563c9469 --- /dev/null +++ b/include/cpp2reflect.h @@ -0,0 +1,833 @@ + +#ifndef CPP2REFLECT_H_CPP2 +#define CPP2REFLECT_H_CPP2 + +#define CPP2_IMPORT_STD Yes + +//=== Cpp2 type declarations ==================================================== + + +#include "cpp2util.h" + +#line 1 "reflect.h2" + +#line 19 "reflect.h2" +namespace cpp2 { + +namespace meta { + +#line 27 "reflect.h2" +class source_position; + +#line 44 "reflect.h2" +class passing_style; + +#line 77 "reflect.h2" +class compiler_services; + +#line 262 "reflect.h2" +class declaration_base; + +#line 304 "reflect.h2" +class declaration; + +#line 389 "reflect.h2" +class function_declaration; + +#line 476 "reflect.h2" +class object_declaration; + +#line 512 "reflect.h2" +class type_declaration; + +#line 647 "reflect.h2" +class alias_declaration; + +#line 986 "reflect.h2" +class value_member_info; + +#line 1415 "reflect.h2" +} + +} + + +//=== Cpp2 type definitions and function declarations =========================== + +#line 1 "reflect.h2" + +// Copyright (c) Herb Sutter +// SPDX-License-Identifier: CC-BY-NC-ND-4.0 + +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +#line 14 "reflect.h2" +//=========================================================================== +// Reflection and meta +//=========================================================================== + +#line 19 "reflect.h2" +namespace cpp2 { + +namespace meta { + +#line 24 "reflect.h2" +using lineno_t = std::int32_t; +using colno_t = std::int32_t; // not int16_t... encountered >80,000 char line during testing + +class CPPFRONTAPI source_position + { + public: lineno_t lineno {1}; // one-based offset into program source + public: colno_t colno {1}; // one-based offset into line + + public: source_position(); + public: source_position(cpp2::in l); +#line 33 "reflect.h2" + public: auto operator=(cpp2::in l) -> source_position& ; + public: source_position(cpp2::in l, cpp2::in c); + +#line 40 "reflect.h2" + public: [[nodiscard]] auto to_string() const& -> std::string; + public: [[nodiscard]] auto operator<=>(source_position const& that) const& -> std::strong_ordering = default; +public: source_position(source_position const& that); + +public: auto operator=(source_position const& that) -> source_position& ; +public: source_position(source_position&& that) noexcept; +public: auto operator=(source_position&& that) noexcept -> source_position& ; + +#line 41 "reflect.h2" +}; + +#line 44 "reflect.h2" +class CPPFRONTAPI passing_style + { +private: cpp2::i8 _value; private: constexpr passing_style(cpp2::in _val); + +private: constexpr auto operator=(cpp2::in _val) -> passing_style& ; +public: static const passing_style in; +public: static const passing_style copy; +public: static const passing_style inout; +public: static const passing_style out; +public: static const passing_style move; +public: static const passing_style forward; +public: static const passing_style invalid; +public: [[nodiscard]] constexpr auto get_raw_value() const& -> cpp2::i8; +public: constexpr explicit passing_style(); +public: constexpr passing_style(passing_style const& that); +public: constexpr auto operator=(passing_style const& that) -> passing_style& ; +public: constexpr passing_style(passing_style&& that) noexcept; +public: constexpr auto operator=(passing_style&& that) noexcept -> passing_style& ; +public: [[nodiscard]] auto operator<=>(passing_style const& that) const& -> std::strong_ordering = default; +public: [[nodiscard]] auto to_string() const& -> std::string; + +#line 53 "reflect.h2" +}; + +// Not `passing_style::from_string` to workaround #555. +[[nodiscard]] auto to_passing_style(cpp2::in s) -> passing_style; + +#line 70 "reflect.h2" +//----------------------------------------------------------------------- +// +// Compiler services +// +//----------------------------------------------------------------------- +// + +class CPPFRONTAPI compiler_services + { + // Common data members + // + private: std::any data_; + private: [[nodiscard]] auto data() const& -> auto&&; + private: [[nodiscard]] auto data() & -> auto&&; + + // Constructor + // + public: explicit compiler_services( + + cpp2::in data_v + ); + +#line 96 "reflect.h2" + // Common API + // + public: auto set_metafunction_name(cpp2::in name, cpp2::in> args) & -> void; + +#line 104 "reflect.h2" + public: [[nodiscard]] auto get_metafunction_name() const& -> std::string_view; + + public: [[nodiscard]] auto get_argument(cpp2::in index) & -> std::string; + +#line 114 "reflect.h2" + public: [[nodiscard]] auto get_arguments() & -> std::vector; + +#line 119 "reflect.h2" + public: [[nodiscard]] auto arguments_were_used() const& -> bool; + + protected: [[nodiscard]] auto parse_statement( + + std::string_view source + ) & -> auto; + +#line 175 "reflect.h2" + public: [[nodiscard]] virtual auto position() const -> source_position; + +#line 181 "reflect.h2" + // Error diagnosis and handling, integrated with compiler output + // Unlike a contract violation, .requires continues further processing + // + public: auto require( + + cpp2::in b, + cpp2::in msg + ) const& -> void; + +#line 195 "reflect.h2" + public: auto error(cpp2::in msg) const& -> void; + +#line 204 "reflect.h2" + // Enable custom contracts on this object, integrated with compiler output + // Unlike .requires, a contract violation stops further processing + // + public: auto report_violation(auto const& msg) const& -> void; + +#line 212 "reflect.h2" + public: [[nodiscard]] auto has_handler() const& -> auto; + public: virtual ~compiler_services() noexcept; +public: compiler_services(compiler_services const& that); + +#line 213 "reflect.h2" +}; + +#line 216 "reflect.h2" +/* +//----------------------------------------------------------------------- +// +// Type IDs +// +//----------------------------------------------------------------------- +// + +// All type_ids are wrappers around a pointer to node +// +type_id: @dll_visible @polymorphic_base @copyable type = +{ + this: compiler_services = (); + + n: type_id_node; + + protected operator=: ( + out this, + n_: type_id_node, + s : compiler_services + ) + = { + compiler_services = s; + n = n_; + assert( n, "a meta::type_id must point to a valid type_id_node, not null" ); + } + + is_wildcard : (this) -> bool = n.is_wildcard(); + is_pointer_qualified: (this) -> bool = n.is_pointer_qualified(); + template_args_count : (this) -> int = n.template_arguments().ssize(); + to_string : (this) -> std::string = n.to_string(); + + position: (override this) -> source_position = n.position(); +} +*/ + +#line 253 "reflect.h2" +//----------------------------------------------------------------------- +// +// Declarations +// +//----------------------------------------------------------------------- +// + +// All declarations are wrappers around a pointer to node +// +class CPPFRONTAPI declaration_base +: public compiler_services { + +#line 266 "reflect.h2" + public: class node_pointer + { + private: void* n; + + public: template node_pointer( + + T const& n_ + ); +#line 270 "reflect.h2" + public: template auto operator=( + + T const& n_ + ) -> node_pointer& ; + +#line 280 "reflect.h2" + public: [[nodiscard]] auto operator*() const& -> auto&&; + public: node_pointer(node_pointer const& that); + +public: auto operator=(node_pointer const& that) -> node_pointer& ; +public: node_pointer(node_pointer&& that) noexcept; +public: auto operator=(node_pointer&& that) noexcept -> node_pointer& ; + +#line 281 "reflect.h2" + }; + + protected: node_pointer n; + + protected: explicit declaration_base( + + cpp2::in n_, + cpp2::in s + ); + +#line 295 "reflect.h2" + public: [[nodiscard]] auto position() const -> source_position override; + + public: [[nodiscard]] auto print() const& -> std::string; + public: virtual ~declaration_base() noexcept; +public: declaration_base(declaration_base const& that); + +#line 298 "reflect.h2" +}; + +#line 301 "reflect.h2" +//----------------------------------------------------------------------- +// All declarations +// +class CPPFRONTAPI declaration +: public declaration_base { + +#line 308 "reflect.h2" + public: explicit declaration( + + cpp2::in n_, + cpp2::in s + ); + +#line 317 "reflect.h2" + public: [[nodiscard]] auto is_public() const& -> bool; + public: [[nodiscard]] auto is_protected() const& -> bool; + public: [[nodiscard]] auto is_private() const& -> bool; + public: [[nodiscard]] auto is_default_access() const& -> bool; + + public: auto default_to_public() & -> void; + public: auto default_to_protected() & -> void; + public: auto default_to_private() & -> void; + + public: [[nodiscard]] auto make_public() & -> bool; + public: [[nodiscard]] auto make_protected() & -> bool; + public: [[nodiscard]] auto make_private() & -> bool; + + public: [[nodiscard]] auto is_dll_visible() const& -> bool; + public: [[nodiscard]] auto make_dll_visible() & -> bool; + + public: [[nodiscard]] auto has_name() const& -> bool; + public: [[nodiscard]] auto has_name(cpp2::in s) const& -> bool; + + public: [[nodiscard]] auto name() const& -> std::string_view; + +#line 341 "reflect.h2" + public: [[nodiscard]] auto has_initializer() const& -> bool; + + public: [[nodiscard]] auto is_global() const& -> bool; + public: [[nodiscard]] auto is_function() const& -> bool; + public: [[nodiscard]] auto is_object() const& -> bool; + public: [[nodiscard]] auto is_base_object() const& -> bool; + public: [[nodiscard]] auto is_member_object() const& -> bool; + public: [[nodiscard]] auto is_type() const& -> bool; + public: [[nodiscard]] auto is_namespace() const& -> bool; + public: [[nodiscard]] auto is_alias() const& -> bool; + + public: [[nodiscard]] auto is_type_alias() const& -> bool; + public: [[nodiscard]] auto is_namespace_alias() const& -> bool; + public: [[nodiscard]] auto is_object_alias() const& -> bool; + + public: [[nodiscard]] auto is_function_expression() const& -> bool; + + public: [[nodiscard]] auto as_function() const& -> function_declaration; + public: [[nodiscard]] auto as_object() const& -> object_declaration; + public: [[nodiscard]] auto as_type() const& -> type_declaration; + public: [[nodiscard]] auto as_alias() const& -> alias_declaration; + + public: [[nodiscard]] auto get_parent() const& -> declaration; + + public: [[nodiscard]] auto parent_is_function() const& -> bool; + public: [[nodiscard]] auto parent_is_object() const& -> bool; + public: [[nodiscard]] auto parent_is_type() const& -> bool; + public: [[nodiscard]] auto parent_is_namespace() const& -> bool; + public: [[nodiscard]] auto parent_is_alias() const& -> bool; + + public: [[nodiscard]] auto parent_is_type_alias() const& -> bool; + public: [[nodiscard]] auto parent_is_namespace_alias() const& -> bool; + public: [[nodiscard]] auto parent_is_object_alias() const& -> bool; + + public: [[nodiscard]] auto parent_is_polymorphic() const& -> bool; + + public: auto mark_for_removal_from_enclosing_type() & -> void; + public: virtual ~declaration() noexcept; +public: declaration(declaration const& that); + + // this precondition should be sufficient ... + +#line 383 "reflect.h2" +}; + +#line 386 "reflect.h2" +//----------------------------------------------------------------------- +// Function declarations +// +class CPPFRONTAPI function_declaration +: public declaration { + +#line 393 "reflect.h2" + public: explicit function_declaration( + + cpp2::in n_, + cpp2::in s + ); + +#line 403 "reflect.h2" + public: [[nodiscard]] auto index_of_parameter_named(cpp2::in s) const& -> int; + public: [[nodiscard]] auto has_parameter_named(cpp2::in s) const& -> bool; + public: [[nodiscard]] auto has_in_parameter_named(cpp2::in s) const& -> bool; + public: [[nodiscard]] auto has_out_parameter_named(cpp2::in s) const& -> bool; + public: [[nodiscard]] auto has_move_parameter_named(cpp2::in s) const& -> bool; + public: [[nodiscard]] auto first_parameter_name() const& -> std::string; + + public: [[nodiscard]] auto has_parameter_with_name_and_pass(cpp2::in s, cpp2::in pass) const& -> bool; + + public: [[nodiscard]] auto is_function_with_this() const& -> bool; + public: [[nodiscard]] auto is_virtual() const& -> bool; + public: [[nodiscard]] auto is_defaultable() const& -> bool; + public: [[nodiscard]] auto is_constructor() const& -> bool; + public: [[nodiscard]] auto is_default_constructor() const& -> bool; + public: [[nodiscard]] auto is_move() const& -> bool; + public: [[nodiscard]] auto is_swap() const& -> bool; + public: [[nodiscard]] auto is_constructor_with_that() const& -> bool; + public: [[nodiscard]] auto is_constructor_with_in_that() const& -> bool; + public: [[nodiscard]] auto is_constructor_with_move_that() const& -> bool; + public: [[nodiscard]] auto is_assignment() const& -> bool; + public: [[nodiscard]] auto is_assignment_with_that() const& -> bool; + public: [[nodiscard]] auto is_assignment_with_in_that() const& -> bool; + public: [[nodiscard]] auto is_assignment_with_move_that() const& -> bool; + public: [[nodiscard]] auto is_destructor() const& -> bool; + + public: [[nodiscard]] auto is_copy_or_move() const& -> bool; + + public: [[nodiscard]] auto has_declared_return_type() const& -> bool; + public: [[nodiscard]] auto has_deduced_return_type() const& -> bool; + public: [[nodiscard]] auto has_bool_return_type() const& -> bool; + public: [[nodiscard]] auto has_non_void_return_type() const& -> bool; + + public: [[nodiscard]] auto unnamed_return_type() const& -> std::string; + + public: [[nodiscard]] auto get_parameters() const& -> std::vector; + +#line 447 "reflect.h2" + public: [[nodiscard]] auto is_binary_comparison_function() const& -> bool; + + public: auto default_to_virtual() & -> void; + + public: [[nodiscard]] auto make_virtual() & -> bool; + + public: auto add_initializer(cpp2::in source) & -> void; + public: function_declaration(function_declaration const& that); + + +#line 470 "reflect.h2" +}; + +#line 473 "reflect.h2" +//----------------------------------------------------------------------- +// Object declarations +// +class CPPFRONTAPI object_declaration +: public declaration { + +#line 480 "reflect.h2" + public: explicit object_declaration( + + cpp2::in n_, + cpp2::in s + ); + +#line 490 "reflect.h2" + public: [[nodiscard]] auto is_const() const& -> bool; + public: [[nodiscard]] auto has_wildcard_type() const& -> bool; + + public: [[nodiscard]] auto type() const& -> std::string; + +#line 500 "reflect.h2" + public: [[nodiscard]] auto initializer() const& -> std::string; + public: object_declaration(object_declaration const& that); + + +#line 506 "reflect.h2" +}; + +#line 509 "reflect.h2" +//----------------------------------------------------------------------- +// Type declarations +// +class CPPFRONTAPI type_declaration +: public declaration { + +#line 516 "reflect.h2" + public: explicit type_declaration( + + cpp2::in n_, + cpp2::in s + ); + +#line 526 "reflect.h2" + public: auto reserve_names(cpp2::in name, auto&& ...etc) const& -> void; + +#line 538 "reflect.h2" + public: [[nodiscard]] auto is_polymorphic() const& -> bool; + public: [[nodiscard]] auto is_final() const& -> bool; + public: [[nodiscard]] auto make_final() & -> bool; + + public: [[nodiscard]] auto get_member_functions() const& -> std::vector; + +#line 553 "reflect.h2" + public: [[nodiscard]] auto get_member_functions_needing_initializer() const& -> std::vector; + +#line 568 "reflect.h2" + public: [[nodiscard]] auto get_member_objects() const& -> std::vector; + +#line 578 "reflect.h2" + public: [[nodiscard]] auto get_member_types() const& -> std::vector; + +#line 588 "reflect.h2" + public: [[nodiscard]] auto get_member_aliases() const& -> std::vector; + +#line 598 "reflect.h2" + public: [[nodiscard]] auto get_members() const& -> std::vector; +struct query_declared_value_set_functions_ret { bool out_this_in_that; bool out_this_move_that; bool inout_this_in_that; bool inout_this_move_that; }; + + + +#line 608 "reflect.h2" + public: [[nodiscard]] auto query_declared_value_set_functions() const& -> query_declared_value_set_functions_ret; + +#line 623 "reflect.h2" + public: auto add_member(cpp2::in source) & -> void; + +#line 637 "reflect.h2" + public: auto remove_marked_members() & -> void; + public: auto remove_all_members() & -> void; + + public: auto disable_member_function_generation() & -> void; + public: type_declaration(type_declaration const& that); + +#line 641 "reflect.h2" +}; + +#line 644 "reflect.h2" +//----------------------------------------------------------------------- +// Alias declarations +// +class CPPFRONTAPI alias_declaration +: public declaration { + +#line 651 "reflect.h2" + public: explicit alias_declaration( + + cpp2::in n_, + cpp2::in s + ); + public: alias_declaration(alias_declaration const& that); + + +#line 660 "reflect.h2" +}; + +#line 663 "reflect.h2" +//----------------------------------------------------------------------- +// +// Metafunctions - these are hardwired for now until we get to the +// step of writing a Cpp2 interpreter to run inside the compiler +// +//----------------------------------------------------------------------- +// + +//----------------------------------------------------------------------- +// Some common metafunction helpers (metafunctions are just functions, +// so they can be factored as usual) +// +auto add_virtual_destructor(meta::type_declaration& t) -> void; + +#line 681 "reflect.h2" +//----------------------------------------------------------------------- +// +// "... an abstract base class defines an interface ..." +// +// -- Stroustrup (The Design and Evolution of C++, 12.3.1) +// +//----------------------------------------------------------------------- +// +// interface +// +// an abstract base class having only pure virtual functions +// +CPPFRONTAPI auto interface(meta::type_declaration& t) -> void; + +#line 720 "reflect.h2" +//----------------------------------------------------------------------- +// +// "C.35: A base class destructor should be either public and +// virtual, or protected and non-virtual." +// +// "[C.43] ... a base class should not be copyable, and so does not +// necessarily need a default constructor." +// +// -- Stroustrup, Sutter, et al. (C++ Core Guidelines) +// +//----------------------------------------------------------------------- +// +// polymorphic_base +// +// A pure polymorphic base type that is not copyable, and whose +// destructor is either public and virtual or protected and nonvirtual. +// +// Unlike an interface, it can have nonpublic and nonvirtual functions. +// +CPPFRONTAPI auto polymorphic_base(meta::type_declaration& t) -> void; + +#line 764 "reflect.h2" +//----------------------------------------------------------------------- +// +// "... A totally ordered type ... requires operator<=> that +// returns std::strong_ordering. If the function is not +// user-written, a lexicographical memberwise implementation +// is generated by default..." +// +// -- P0707R4, section 3 +// +// Note: This feature derived from Cpp2 was already adopted +// into Standard C++ via paper P0515, so most of the +// heavy lifting is done by the Cpp1 C++20/23 compiler, +// including the memberwise default semantics +// (In contrast, cppfront has to do the work itself for +// default memberwise semantics for operator= assignment +// as those aren't yet part of Standard C++) +// +//----------------------------------------------------------------------- +// + +auto ordered_impl( + meta::type_declaration& t, + cpp2::in ordering// must be "strong_ordering" etc. +) -> void; + +#line 808 "reflect.h2" +//----------------------------------------------------------------------- +// ordered - a totally ordered type +// +// Note: the ordering that should be encouraged as default gets the nice name +// +CPPFRONTAPI auto ordered(meta::type_declaration& t) -> void; + +#line 818 "reflect.h2" +//----------------------------------------------------------------------- +// weakly_ordered - a weakly ordered type +// +CPPFRONTAPI auto weakly_ordered(meta::type_declaration& t) -> void; + +#line 826 "reflect.h2" +//----------------------------------------------------------------------- +// partially_ordered - a partially ordered type +// +CPPFRONTAPI auto partially_ordered(meta::type_declaration& t) -> void; + +#line 835 "reflect.h2" +//----------------------------------------------------------------------- +// +// "A value is ... a regular type. It must have all public +// default construction, copy/move construction/assignment, +// and destruction, all of which are generated by default +// if not user-written; and it must not have any protected +// or virtual functions (including the destructor)." +// +// -- P0707R4, section 3 +// +//----------------------------------------------------------------------- +// +// copyable +// +// A type with (copy and move) x (construction and assignment) +// +CPPFRONTAPI auto copyable(meta::type_declaration& t) -> void; + +#line 872 "reflect.h2" +//----------------------------------------------------------------------- +// +// basic_value +// +// A regular type: copyable, plus has public default construction +// and no protected or virtual functions +// +CPPFRONTAPI auto basic_value(meta::type_declaration& t) -> void; + +#line 897 "reflect.h2" +//----------------------------------------------------------------------- +// +// "A 'value' is a totally ordered basic_value..." +// +// -- P0707R4, section 3 +// +// value - a value type that is totally ordered +// +// Note: the ordering that should be encouraged as default gets the nice name +// +CPPFRONTAPI auto value(meta::type_declaration& t) -> void; + +#line 913 "reflect.h2" +CPPFRONTAPI auto weakly_ordered_value(meta::type_declaration& t) -> void; + +#line 919 "reflect.h2" +CPPFRONTAPI auto partially_ordered_value(meta::type_declaration& t) -> void; + +#line 926 "reflect.h2" +//----------------------------------------------------------------------- +// +// "By definition, a `struct` is a `class` in which members +// are by default `public`; that is, +// +// struct s { ... +// +// is simply shorthand for +// +// class s { public: ... +// +// ... Which style you use depends on circumstances and taste. +// I usually prefer to use `struct` for classes that have all +// data `public`." +// +// -- Stroustrup (The C++ Programming Language, 3rd ed., p. 234) +// +//----------------------------------------------------------------------- +// +// struct +// +// a type with only public bases, objects, and functions, +// no virtual functions, and no user-defined constructors +// (i.e., no invariants) or assignment or destructors. +// +CPPFRONTAPI auto cpp2_struct(meta::type_declaration& t) -> void; + +#line 969 "reflect.h2" +//----------------------------------------------------------------------- +// +// "C enumerations constitute a curiously half-baked concept. ... +// the cleanest way out was to deem each enumeration a separate type." +// +// -- Stroustrup (The Design and Evolution of C++, 11.7) +// +// "An enumeration is a distinct type ... with named constants" +// +// -- ISO C++ Standard +// +//----------------------------------------------------------------------- +// +// basic_enum +// +// a type together with named constants that are its possible values +// +class value_member_info { + public: std::string name; + public: std::string type; + public: std::string value; +}; + +auto basic_enum( + meta::type_declaration& t, + auto const& nextval, + cpp2::in bitwise + ) -> void; + +#line 1175 "reflect.h2" +//----------------------------------------------------------------------- +// +// "An enum[...] is a totally ordered value type that stores a +// value of its enumerators's type, and otherwise has only public +// member variables of its enumerator's type, all of which are +// naturally scoped because they are members of a type." +// +// -- P0707R4, section 3 +// +CPPFRONTAPI auto cpp2_enum(meta::type_declaration& t) -> void; + +#line 1201 "reflect.h2" +//----------------------------------------------------------------------- +// +// "flag_enum expresses an enumeration that stores values +// corresponding to bitwise-or'd enumerators. The enumerators must +// be powers of two, and are automatically generated [...] A none +// value is provided [...] Operators | and & are provided to +// combine and extract values." +// +// -- P0707R4, section 3 +// +CPPFRONTAPI auto flag_enum(meta::type_declaration& t) -> void; + +#line 1233 "reflect.h2" +//----------------------------------------------------------------------- +// +// "As with void*, programmers should know that unions [...] are +// inherently dangerous, should be avoided wherever possible, +// and should be handled with special care when actually needed." +// +// -- Stroustrup (The Design and Evolution of C++, 14.3.4.1) +// +// "C++17 needs a type-safe union... The implications of the +// consensus `variant` design are well understood and have been +// explored over several LEWG discussions, over a thousand emails, +// a joint LEWG/EWG session, and not to mention 12 years of +// experience with Boost and other libraries." +// +// -- Axel Naumann, in P0088 (wg21.link/p0088), +// the adopted proposal for C++17 std::variant +// +//----------------------------------------------------------------------- +// +// union +// +// a type that contains exactly one of a fixed set of values at a time +// + +CPPFRONTAPI auto cpp2_union(meta::type_declaration& t) -> void; + +#line 1389 "reflect.h2" +//----------------------------------------------------------------------- +// +// print - output a pretty-printed visualization of t +// +CPPFRONTAPI auto print(cpp2::in t) -> void; + +#line 1399 "reflect.h2" +//----------------------------------------------------------------------- +// +// dll_visible - makes t visible in a DLL +// +CPPFRONTAPI auto dll_visible(meta::type_declaration& t) -> void; + +#line 1408 "reflect.h2" +CPPFRONTAPI auto dll_visible(meta::function_declaration& t) -> void; + +#line 1415 "reflect.h2" +} + +} + +#endif diff --git a/include/cpp2util.h b/include/cpp2util.h index 463dd994e..054d608bb 100644 --- a/include/cpp2util.h +++ b/include/cpp2util.h @@ -283,6 +283,15 @@ #endif +#if defined(_WIN32) +#define CPPFRONTAPI __declspec(dllexport) +#else +#define CPPFRONTAPI __attribute__ ((visibility ("default"))) +#endif + +#define CPP2_C_API extern "C" CPPFRONTAPI + + namespace cpp2 { @@ -388,8 +397,8 @@ struct String #define CPP2_MESSAGE_PARAM char const* #define CPP2_CONTRACT_MSG cpp2::message_to_cstr_adapter -auto message_to_cstr_adapter( CPP2_MESSAGE_PARAM msg ) -> CPP2_MESSAGE_PARAM { return msg ? msg : ""; } -auto message_to_cstr_adapter( std::string const& msg ) -> CPP2_MESSAGE_PARAM { return msg.c_str(); } +inline auto message_to_cstr_adapter( CPP2_MESSAGE_PARAM msg ) -> CPP2_MESSAGE_PARAM { return msg ? msg : ""; } +inline auto message_to_cstr_adapter( std::string const& msg ) -> CPP2_MESSAGE_PARAM { return msg.c_str(); } class contract_group { public: @@ -1111,7 +1120,9 @@ inline constexpr auto is( auto const& x, auto&& value ) -> bool else if constexpr (requires{ bool{x == value}; }) { return x == value; } - return false; + else { + return false; + } } @@ -1996,4 +2007,41 @@ using cpp2::cpp2_new; #define CPP2_REQUIRES_(...) requires (__VA_ARGS__) #endif +#ifdef _MSC_VER +#pragma warning(push) +#pragma warning(disable: 4251) +#endif + +#include "cpp2reflect.h" + +// FIXME: Should be moved to somewhere inside `cpp2reflect.h` but idk how yet. +namespace cpp2::meta { + +using mf_signature_in = void(cpp2::in); +using mf_signature_inout = void(type_declaration&); + +struct register_function { + register_function(const char* name, mf_signature_in* f); + register_function(const char* name, mf_signature_inout* f); + ~register_function(); // TODO: Remove, used for testing. +}; + +// FIXME: These are *internal* to cppfront, and should never be exposed to users. +using mf = std::variant; + +struct record +{ + std::string fully_qualified_name; + mf function; + // TODO: Extra info to give better diagnostics to the user? + // std::string where_its_defined; + // int line, column; +}; + +} + +#ifdef _MSC_VER +#pragma warning(pop) +#endif + #endif diff --git a/regression-tests/test-results/mixed-allcpp1-hello.cpp b/regression-tests/test-results/mixed-allcpp1-hello.cpp index d4aa9ded2..cafec20bb 100644 --- a/regression-tests/test-results/mixed-allcpp1-hello.cpp +++ b/regression-tests/test-results/mixed-allcpp1-hello.cpp @@ -1,3 +1,4 @@ +#line 1 "mixed-allcpp1-hello.cpp2" // Step 1: rename .cpp to .cpp2 diff --git a/regression-tests/test-results/mixed-intro-example-three-loops.cpp b/regression-tests/test-results/mixed-intro-example-three-loops.cpp index bfe37f8c3..c246eff5d 100644 --- a/regression-tests/test-results/mixed-intro-example-three-loops.cpp +++ b/regression-tests/test-results/mixed-intro-example-three-loops.cpp @@ -1,3 +1,4 @@ +#line 1 "mixed-intro-example-three-loops.cpp2" #include #include #include diff --git a/regression-tests/test-results/mixed-postexpression-with-capture.cpp b/regression-tests/test-results/mixed-postexpression-with-capture.cpp index 1994bdca0..2efff94e3 100644 --- a/regression-tests/test-results/mixed-postexpression-with-capture.cpp +++ b/regression-tests/test-results/mixed-postexpression-with-capture.cpp @@ -38,7 +38,6 @@ struct make_strings_ret { std::string a; std::string b; }; [[nodiscard]] auto make_strings() -> make_strings_ret; #line 38 "mixed-postexpression-with-capture.cpp2" -#line 1 "mixed-postexpression-with-capture.cpp2" //=== Cpp2 function definitions ================================================= diff --git a/regression-tests/test-results/pure2-bounds-safety-span.cpp b/regression-tests/test-results/pure2-bounds-safety-span.cpp index 17966588e..c28c75d9e 100644 --- a/regression-tests/test-results/pure2-bounds-safety-span.cpp +++ b/regression-tests/test-results/pure2-bounds-safety-span.cpp @@ -20,7 +20,6 @@ auto print_and_decorate(auto const& thing) -> void; #line 17 "pure2-bounds-safety-span.cpp2" -#line 1 "pure2-bounds-safety-span.cpp2" //=== Cpp2 function definitions ================================================= diff --git a/regression-tests/test-results/pure2-intro-example-three-loops.cpp b/regression-tests/test-results/pure2-intro-example-three-loops.cpp index 1d827be97..a5e79313d 100644 --- a/regression-tests/test-results/pure2-intro-example-three-loops.cpp +++ b/regression-tests/test-results/pure2-intro-example-three-loops.cpp @@ -23,7 +23,6 @@ auto decorate_and_print(auto& thing) -> void; [[nodiscard]] auto main() -> int; #line 30 "pure2-intro-example-three-loops.cpp2" -#line 1 "pure2-intro-example-three-loops.cpp2" //=== Cpp2 function definitions ================================================= diff --git a/regression-tests/test-results/pure2-stdio.cpp b/regression-tests/test-results/pure2-stdio.cpp index 4ae3e58ae..2379152a4 100644 --- a/regression-tests/test-results/pure2-stdio.cpp +++ b/regression-tests/test-results/pure2-stdio.cpp @@ -19,7 +19,6 @@ [[nodiscard]] auto main() -> int; #line 10 "pure2-stdio.cpp2" -#line 1 "pure2-stdio.cpp2" //=== Cpp2 function definitions ================================================= diff --git a/regression-tests/test-results/pure2-type-safety-1.cpp b/regression-tests/test-results/pure2-type-safety-1.cpp index 5475cb418..c1b98df68 100644 --- a/regression-tests/test-results/pure2-type-safety-1.cpp +++ b/regression-tests/test-results/pure2-type-safety-1.cpp @@ -23,7 +23,6 @@ auto test_generic(auto const& x, auto const& msg) -> void; auto print(cpp2::in msg, cpp2::in b) -> void; #line 35 "pure2-type-safety-1.cpp2" -#line 1 "pure2-type-safety-1.cpp2" //=== Cpp2 function definitions ================================================= diff --git a/source/common.h b/source/common.h index 660e72be1..554e1afd5 100644 --- a/source/common.h +++ b/source/common.h @@ -12,7 +12,7 @@ #ifdef _MSC_VER -#pragma warning(disable: 4456) +#pragma warning(disable: 4456 4996) #endif #include "cpp2util.h" @@ -86,27 +86,10 @@ struct source_line }; -using lineno_t = int32_t; -using colno_t = int32_t; // not int16_t... encountered >80,000 char line during testing +using cpp2::meta::lineno_t; +using cpp2::meta::colno_t; -struct source_position -{ - lineno_t lineno; // one-based offset into program source - colno_t colno; // one-based offset into line - - source_position(lineno_t l = 1, colno_t c = 1 ) - : lineno{ l }, colno{ c } - { - } - - auto operator<=>(source_position const&) const = default; - - auto to_string() const - -> std::string - { - return "(" + std::to_string(lineno) + "," + std::to_string(colno) + ")"; - } -}; +using cpp2::meta::source_position; struct comment { @@ -127,7 +110,7 @@ struct string_parts { string_parts(const std::string& beginseq, const std::string& endseq, - adds_sequences strateg) + adds_sequences strateg) : begin_seq{beginseq} , end_seq{endseq} , strategy{strateg} @@ -144,16 +127,16 @@ struct string_parts { void clear() { parts.clear(); } auto generate() const -> std::string { - - if (parts.empty()) { - return (strategy & on_the_beginning ? begin_seq : std::string{}) - + (strategy & on_the_end ? end_seq : std::string{}); + + if (parts.empty()) { + return (strategy & on_the_beginning ? begin_seq : std::string{}) + + (strategy & on_the_end ? end_seq : std::string{}); } - auto result = std::visit(begin_visit{begin_seq, strategy}, + auto result = std::visit(begin_visit{begin_seq, strategy}, parts.front()); - if (std::ssize(parts) > 1) { + if (std::ssize(parts) > 1) { auto it1 = parts.cbegin(); auto it2 = parts.cbegin()+1; for(;it2 != parts.cend(); ++it1, ++it2) { @@ -466,6 +449,18 @@ auto to_upper(char c) } +auto to_lower(char c) + -> char +{ + // C toupper is only not-UB in [0,127] and returns the wrong type, + // so wrap the range check and the type cast here in one place... + // note the 126 (not 127) is intentional to avoid a GCC warning + if (0 <= c && c <= 126) { return static_cast(std::tolower(c)); } + // else + return c; +} + + auto to_upper_and_underbar(std::string_view s) -> std::string { @@ -478,6 +473,36 @@ auto to_upper_and_underbar(std::string_view s) } +auto to_lower_and_collapsed_underbar( + std::string_view s, + bool prev_was_underbar = false, + bool drop_back_underbar = false + ) + -> std::string +{ + auto ret = std::string{}; + for (char c : s) { + if (std::isalnum(c)) { + ret.push_back(to_lower(c)); + prev_was_underbar = false; + } + else if (!prev_was_underbar) { + ret.push_back('_'); + prev_was_underbar = true; + } + } + if ( + drop_back_underbar + && !ret.empty() + && ret.back() == '_' + ) + { + ret.pop_back(); + } + return ret; +} + + auto is_empty_or_a_decimal_number(std::string_view s) -> bool { @@ -918,7 +943,7 @@ class stackinstr static auto print(auto&& ee, std::string_view label) { std::cout << "\n=== Stack debug information: " << label << " stack ===\n"; - for (auto& e: ee) + for (auto& e: ee) if (e.ptr) { std::cout << " " << std::setw(6) diff --git a/source/reflect.h b/source/cpp2reflect.hpp similarity index 59% rename from source/reflect.h rename to source/cpp2reflect.hpp index 1cb66f794..f7565cb78 100644 --- a/source/reflect.h +++ b/source/cpp2reflect.hpp @@ -1,814 +1,161 @@ -#ifndef REFLECT_H_CPP2 -#define REFLECT_H_CPP2 +#ifndef CPP2REFLECT_H_CPP2 +#error This file is part of a '.h2' header compiled to be consumed from another -pure-cpp2 file. To use this file, write '#include "cpp2reflect.h2"' in a '.h2' or '.cpp2' file compiled with -pure-cpp2. +#endif +#ifndef CPP2REFLECT_HPP_CPP2 +#define CPP2REFLECT_HPP_CPP2 -//=== Cpp2 type declarations ==================================================== -#include "cpp2util.h" +//=== Cpp2 function definitions ================================================= #line 1 "reflect.h2" -#line 20 "reflect.h2" +#line 19 "reflect.h2" namespace cpp2 { namespace meta { #line 32 "reflect.h2" -class compiler_services; - -#line 223 "reflect.h2" -class declaration_base; - -#line 249 "reflect.h2" -class declaration; - -#line 331 "reflect.h2" -class function_declaration; - -#line 418 "reflect.h2" -class object_declaration; - -#line 454 "reflect.h2" -class type_declaration; - -#line 589 "reflect.h2" -class alias_declaration; - -#line 928 "reflect.h2" -class value_member_info; - -#line 1445 "reflect.h2" -} - -} - - -//=== Cpp2 type definitions and function declarations =========================== - -#line 1 "reflect.h2" - -// Copyright (c) Herb Sutter -// SPDX-License-Identifier: CC-BY-NC-ND-4.0 - -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. - - -//=========================================================================== -// Reflection and meta -//=========================================================================== - -#include "parse.h" - -#line 20 "reflect.h2" -namespace cpp2 { - -namespace meta { - -#line 25 "reflect.h2" -//----------------------------------------------------------------------- -// -// Compiler services -// -//----------------------------------------------------------------------- -// - -class compiler_services - { - // Common data members - // - private: std::vector* errors; - private: int errors_original_size; - private: std::deque* generated_tokens; - private: cpp2::parser parser; - private: std::string metafunction_name {}; - private: std::vector metafunction_args {}; - private: bool metafunctions_used {false}; - - // Constructor - // - public: explicit compiler_services( - - std::vector* errors_, - std::deque* generated_tokens_ - ); - -#line 58 "reflect.h2" - // Common API - // - public: auto set_metafunction_name(cpp2::in name, cpp2::in> args) & -> void; - -#line 66 "reflect.h2" - public: [[nodiscard]] auto get_metafunction_name() const& -> std::string_view; - - public: [[nodiscard]] auto get_argument(cpp2::in index) & -> std::string; - -#line 76 "reflect.h2" - public: [[nodiscard]] auto get_arguments() & -> std::vector; - -#line 81 "reflect.h2" - public: [[nodiscard]] auto arguments_were_used() const& -> bool; -using parse_statement_ret = std::unique_ptr; - - -#line 83 "reflect.h2" - protected: [[nodiscard]] auto parse_statement( - - std::string_view source - ) & -> parse_statement_ret; - -#line 136 "reflect.h2" - public: [[nodiscard]] virtual auto position() const -> source_position; - -#line 142 "reflect.h2" - // Error diagnosis and handling, integrated with compiler output - // Unlike a contract violation, .requires continues further processing - // - public: auto require( - - cpp2::in b, - cpp2::in msg - ) const& -> void; - -#line 156 "reflect.h2" - public: auto error(cpp2::in msg) const& -> void; - -#line 165 "reflect.h2" - // Enable custom contracts on this object, integrated with compiler output - // Unlike .requires, a contract violation stops further processing - // - public: auto report_violation(auto const& msg) const& -> void; - -#line 173 "reflect.h2" - public: [[nodiscard]] auto has_handler() const& -> auto; - public: virtual ~compiler_services() noexcept; -public: compiler_services(compiler_services const& that); - -#line 174 "reflect.h2" -}; - -#line 177 "reflect.h2" -/* -//----------------------------------------------------------------------- -// -// Type IDs -// -//----------------------------------------------------------------------- -// - -// All type_ids are wrappers around a pointer to node -// -type_id: @polymorphic_base @copyable type = -{ - this: compiler_services = (); - - n: type_id_node; + source_position::source_position(){} + source_position::source_position(cpp2::in l) + : lineno{ l } { } +#line 33 "reflect.h2" + auto source_position::operator=(cpp2::in l) -> source_position& { + lineno = l; + colno = 1; + return *this; } +#line 34 "reflect.h2" + source_position::source_position(cpp2::in l, cpp2::in c) + : lineno{ l } + , colno{ c } +#line 35 "reflect.h2" + { - protected operator=: ( - out this, - n_: type_id_node, - s : compiler_services - ) - = { - compiler_services = s; - n = n_; - assert( n, "a meta::type_id must point to a valid type_id_node, not null" ); +#line 38 "reflect.h2" } - is_wildcard : (this) -> bool = n.is_wildcard(); - is_pointer_qualified: (this) -> bool = n.is_pointer_qualified(); - template_args_count : (this) -> int = n.template_arguments().ssize(); - to_string : (this) -> std::string = n.to_string(); - - position: (override this) -> source_position = n.position(); -} -*/ - -#line 214 "reflect.h2" -//----------------------------------------------------------------------- -// -// Declarations -// -//----------------------------------------------------------------------- -// - -// All declarations are wrappers around a pointer to node -// -class declaration_base -: public compiler_services { - -#line 227 "reflect.h2" - protected: declaration_node* n; - - protected: explicit declaration_base( - - declaration_node* n_, - cpp2::in s - ); - -#line 240 "reflect.h2" - public: [[nodiscard]] auto position() const -> source_position override; - - public: [[nodiscard]] auto print() const& -> std::string; - public: virtual ~declaration_base() noexcept; -public: declaration_base(declaration_base const& that); - -#line 243 "reflect.h2" -}; - -#line 246 "reflect.h2" -//----------------------------------------------------------------------- -// All declarations -// -class declaration -: public declaration_base { - -#line 253 "reflect.h2" - public: explicit declaration( - - declaration_node* n_, - cpp2::in s - ); - -#line 262 "reflect.h2" - public: [[nodiscard]] auto is_public() const& -> bool; - public: [[nodiscard]] auto is_protected() const& -> bool; - public: [[nodiscard]] auto is_private() const& -> bool; - public: [[nodiscard]] auto is_default_access() const& -> bool; - - public: auto default_to_public() & -> void; - public: auto default_to_protected() & -> void; - public: auto default_to_private() & -> void; - - public: [[nodiscard]] auto make_public() & -> bool; - public: [[nodiscard]] auto make_protected() & -> bool; - public: [[nodiscard]] auto make_private() & -> bool; - - public: [[nodiscard]] auto has_name() const& -> bool; - public: [[nodiscard]] auto has_name(cpp2::in s) const& -> bool; - - public: [[nodiscard]] auto name() const& -> std::string_view; - -#line 283 "reflect.h2" - public: [[nodiscard]] auto has_initializer() const& -> bool; - - public: [[nodiscard]] auto is_global() const& -> bool; - public: [[nodiscard]] auto is_function() const& -> bool; - public: [[nodiscard]] auto is_object() const& -> bool; - public: [[nodiscard]] auto is_base_object() const& -> bool; - public: [[nodiscard]] auto is_member_object() const& -> bool; - public: [[nodiscard]] auto is_type() const& -> bool; - public: [[nodiscard]] auto is_namespace() const& -> bool; - public: [[nodiscard]] auto is_alias() const& -> bool; - - public: [[nodiscard]] auto is_type_alias() const& -> bool; - public: [[nodiscard]] auto is_namespace_alias() const& -> bool; - public: [[nodiscard]] auto is_object_alias() const& -> bool; - - public: [[nodiscard]] auto is_function_expression() const& -> bool; - - public: [[nodiscard]] auto as_function() const& -> function_declaration; - public: [[nodiscard]] auto as_object() const& -> object_declaration; - public: [[nodiscard]] auto as_type() const& -> type_declaration; - public: [[nodiscard]] auto as_alias() const& -> alias_declaration; - - public: [[nodiscard]] auto get_parent() const& -> declaration; - - public: [[nodiscard]] auto parent_is_function() const& -> bool; - public: [[nodiscard]] auto parent_is_object() const& -> bool; - public: [[nodiscard]] auto parent_is_type() const& -> bool; - public: [[nodiscard]] auto parent_is_namespace() const& -> bool; - public: [[nodiscard]] auto parent_is_alias() const& -> bool; - - public: [[nodiscard]] auto parent_is_type_alias() const& -> bool; - public: [[nodiscard]] auto parent_is_namespace_alias() const& -> bool; - public: [[nodiscard]] auto parent_is_object_alias() const& -> bool; - - public: [[nodiscard]] auto parent_is_polymorphic() const& -> bool; - - public: auto mark_for_removal_from_enclosing_type() & -> void; - public: virtual ~declaration() noexcept; -public: declaration(declaration const& that); - - // this precondition should be sufficient ... - -#line 325 "reflect.h2" -}; - -#line 328 "reflect.h2" -//----------------------------------------------------------------------- -// Function declarations -// -class function_declaration -: public declaration { - -#line 335 "reflect.h2" - public: explicit function_declaration( - - declaration_node* n_, - cpp2::in s - ); - -#line 345 "reflect.h2" - public: [[nodiscard]] auto index_of_parameter_named(cpp2::in s) const& -> int; - public: [[nodiscard]] auto has_parameter_named(cpp2::in s) const& -> bool; - public: [[nodiscard]] auto has_in_parameter_named(cpp2::in s) const& -> bool; - public: [[nodiscard]] auto has_out_parameter_named(cpp2::in s) const& -> bool; - public: [[nodiscard]] auto has_move_parameter_named(cpp2::in s) const& -> bool; - public: [[nodiscard]] auto first_parameter_name() const& -> std::string; - - public: [[nodiscard]] auto has_parameter_with_name_and_pass(cpp2::in s, cpp2::in pass) const& -> bool; - - public: [[nodiscard]] auto is_function_with_this() const& -> bool; - public: [[nodiscard]] auto is_virtual() const& -> bool; - public: [[nodiscard]] auto is_defaultable() const& -> bool; - public: [[nodiscard]] auto is_constructor() const& -> bool; - public: [[nodiscard]] auto is_default_constructor() const& -> bool; - public: [[nodiscard]] auto is_move() const& -> bool; - public: [[nodiscard]] auto is_swap() const& -> bool; - public: [[nodiscard]] auto is_constructor_with_that() const& -> bool; - public: [[nodiscard]] auto is_constructor_with_in_that() const& -> bool; - public: [[nodiscard]] auto is_constructor_with_move_that() const& -> bool; - public: [[nodiscard]] auto is_assignment() const& -> bool; - public: [[nodiscard]] auto is_assignment_with_that() const& -> bool; - public: [[nodiscard]] auto is_assignment_with_in_that() const& -> bool; - public: [[nodiscard]] auto is_assignment_with_move_that() const& -> bool; - public: [[nodiscard]] auto is_destructor() const& -> bool; - - public: [[nodiscard]] auto is_copy_or_move() const& -> bool; - - public: [[nodiscard]] auto has_declared_return_type() const& -> bool; - public: [[nodiscard]] auto has_deduced_return_type() const& -> bool; - public: [[nodiscard]] auto has_bool_return_type() const& -> bool; - public: [[nodiscard]] auto has_non_void_return_type() const& -> bool; - - public: [[nodiscard]] auto unnamed_return_type() const& -> std::string; - - public: [[nodiscard]] auto get_parameters() const& -> std::vector; - -#line 389 "reflect.h2" - public: [[nodiscard]] auto is_binary_comparison_function() const& -> bool; - - public: auto default_to_virtual() & -> void; - - public: [[nodiscard]] auto make_virtual() & -> bool; - - public: auto add_initializer(cpp2::in source) & -> void; - public: function_declaration(function_declaration const& that); - - -#line 412 "reflect.h2" -}; - -#line 415 "reflect.h2" -//----------------------------------------------------------------------- -// Object declarations -// -class object_declaration -: public declaration { - -#line 422 "reflect.h2" - public: explicit object_declaration( - - declaration_node* n_, - cpp2::in s - ); - -#line 432 "reflect.h2" - public: [[nodiscard]] auto is_const() const& -> bool; - public: [[nodiscard]] auto has_wildcard_type() const& -> bool; - - public: [[nodiscard]] auto type() const& -> std::string; - -#line 442 "reflect.h2" - public: [[nodiscard]] auto initializer() const& -> std::string; - public: object_declaration(object_declaration const& that); - - -#line 448 "reflect.h2" -}; - -#line 451 "reflect.h2" -//----------------------------------------------------------------------- -// Type declarations -// -class type_declaration -: public declaration { - -#line 458 "reflect.h2" - public: explicit type_declaration( - - declaration_node* n_, - cpp2::in s - ); - -#line 468 "reflect.h2" - public: auto reserve_names(cpp2::in name, auto&& ...etc) const& -> void; - -#line 480 "reflect.h2" - public: [[nodiscard]] auto is_polymorphic() const& -> bool; - public: [[nodiscard]] auto is_final() const& -> bool; - public: [[nodiscard]] auto make_final() & -> bool; - - public: [[nodiscard]] auto get_member_functions() const& -> std::vector; - -#line 495 "reflect.h2" - public: [[nodiscard]] auto get_member_functions_needing_initializer() const& -> std::vector; - -#line 510 "reflect.h2" - public: [[nodiscard]] auto get_member_objects() const& -> std::vector; - -#line 520 "reflect.h2" - public: [[nodiscard]] auto get_member_types() const& -> std::vector; - -#line 530 "reflect.h2" - public: [[nodiscard]] auto get_member_aliases() const& -> std::vector; - -#line 540 "reflect.h2" - public: [[nodiscard]] auto get_members() const& -> std::vector; -struct query_declared_value_set_functions_ret { bool out_this_in_that; bool out_this_move_that; bool inout_this_in_that; bool inout_this_move_that; }; - - - -#line 550 "reflect.h2" - public: [[nodiscard]] auto query_declared_value_set_functions() const& -> query_declared_value_set_functions_ret; - -#line 565 "reflect.h2" - public: auto add_member(cpp2::in source) & -> void; - -#line 579 "reflect.h2" - public: auto remove_marked_members() & -> void; - public: auto remove_all_members() & -> void; - - public: auto disable_member_function_generation() & -> void; - public: type_declaration(type_declaration const& that); - -#line 583 "reflect.h2" -}; - -#line 586 "reflect.h2" -//----------------------------------------------------------------------- -// Alias declarations -// -class alias_declaration -: public declaration { - -#line 593 "reflect.h2" - public: explicit alias_declaration( - - declaration_node* n_, - cpp2::in s - ); - public: alias_declaration(alias_declaration const& that); - - -#line 602 "reflect.h2" -}; - -#line 605 "reflect.h2" -//----------------------------------------------------------------------- -// -// Metafunctions - these are hardwired for now until we get to the -// step of writing a Cpp2 interpreter to run inside the compiler -// -//----------------------------------------------------------------------- -// - -//----------------------------------------------------------------------- -// Some common metafunction helpers (metafunctions are just functions, -// so they can be factored as usual) -// -auto add_virtual_destructor(meta::type_declaration& t) -> void; - -#line 623 "reflect.h2" -//----------------------------------------------------------------------- -// -// "... an abstract base class defines an interface ..." -// -// -- Stroustrup (The Design and Evolution of C++, 12.3.1) -// -//----------------------------------------------------------------------- -// -// interface -// -// an abstract base class having only pure virtual functions -// -auto interface(meta::type_declaration& t) -> void; - -#line 662 "reflect.h2" -//----------------------------------------------------------------------- -// -// "C.35: A base class destructor should be either public and -// virtual, or protected and non-virtual." -// -// "[C.43] ... a base class should not be copyable, and so does not -// necessarily need a default constructor." -// -// -- Stroustrup, Sutter, et al. (C++ Core Guidelines) -// -//----------------------------------------------------------------------- -// -// polymorphic_base -// -// A pure polymorphic base type that is not copyable, and whose -// destructor is either public and virtual or protected and nonvirtual. -// -// Unlike an interface, it can have nonpublic and nonvirtual functions. -// -auto polymorphic_base(meta::type_declaration& t) -> void; - -#line 706 "reflect.h2" -//----------------------------------------------------------------------- -// -// "... A totally ordered type ... requires operator<=> that -// returns std::strong_ordering. If the function is not -// user-written, a lexicographical memberwise implementation -// is generated by default..." -// -// -- P0707R4, section 3 -// -// Note: This feature derived from Cpp2 was already adopted -// into Standard C++ via paper P0515, so most of the -// heavy lifting is done by the Cpp1 C++20/23 compiler, -// including the memberwise default semantics -// (In contrast, cppfront has to do the work itself for -// default memberwise semantics for operator= assignment -// as those aren't yet part of Standard C++) -// -//----------------------------------------------------------------------- -// - -auto ordered_impl( - meta::type_declaration& t, - cpp2::in ordering// must be "strong_ordering" etc. -) -> void; - -#line 750 "reflect.h2" -//----------------------------------------------------------------------- -// ordered - a totally ordered type -// -// Note: the ordering that should be encouraged as default gets the nice name -// -auto ordered(meta::type_declaration& t) -> void; - -#line 760 "reflect.h2" -//----------------------------------------------------------------------- -// weakly_ordered - a weakly ordered type -// -auto weakly_ordered(meta::type_declaration& t) -> void; - -#line 768 "reflect.h2" -//----------------------------------------------------------------------- -// partially_ordered - a partially ordered type -// -auto partially_ordered(meta::type_declaration& t) -> void; - -#line 777 "reflect.h2" -//----------------------------------------------------------------------- -// -// "A value is ... a regular type. It must have all public -// default construction, copy/move construction/assignment, -// and destruction, all of which are generated by default -// if not user-written; and it must not have any protected -// or virtual functions (including the destructor)." -// -// -- P0707R4, section 3 -// -//----------------------------------------------------------------------- -// -// copyable -// -// A type with (copy and move) x (construction and assignment) -// -auto copyable(meta::type_declaration& t) -> void; - -#line 814 "reflect.h2" -//----------------------------------------------------------------------- -// -// basic_value -// -// A regular type: copyable, plus has public default construction -// and no protected or virtual functions -// -auto basic_value(meta::type_declaration& t) -> void; - -#line 839 "reflect.h2" -//----------------------------------------------------------------------- -// -// "A 'value' is a totally ordered basic_value..." -// -// -- P0707R4, section 3 -// -// value - a value type that is totally ordered -// -// Note: the ordering that should be encouraged as default gets the nice name -// -auto value(meta::type_declaration& t) -> void; - -#line 855 "reflect.h2" -auto weakly_ordered_value(meta::type_declaration& t) -> void; - -#line 861 "reflect.h2" -auto partially_ordered_value(meta::type_declaration& t) -> void; - -#line 868 "reflect.h2" -//----------------------------------------------------------------------- -// -// "By definition, a `struct` is a `class` in which members -// are by default `public`; that is, -// -// struct s { ... -// -// is simply shorthand for -// -// class s { public: ... -// -// ... Which style you use depends on circumstances and taste. -// I usually prefer to use `struct` for classes that have all -// data `public`." -// -// -- Stroustrup (The C++ Programming Language, 3rd ed., p. 234) -// -//----------------------------------------------------------------------- -// -// struct -// -// a type with only public bases, objects, and functions, -// no virtual functions, and no user-defined constructors -// (i.e., no invariants) or assignment or destructors. -// -auto cpp2_struct(meta::type_declaration& t) -> void; - -#line 911 "reflect.h2" -//----------------------------------------------------------------------- -// -// "C enumerations constitute a curiously half-baked concept. ... -// the cleanest way out was to deem each enumeration a separate type." -// -// -- Stroustrup (The Design and Evolution of C++, 11.7) -// -// "An enumeration is a distinct type ... with named constants" -// -// -- ISO C++ Standard -// -//----------------------------------------------------------------------- -// -// basic_enum -// -// a type together with named constants that are its possible values -// -class value_member_info { - public: std::string name; - public: std::string type; - public: std::string value; -}; - -auto basic_enum( - meta::type_declaration& t, - auto const& nextval, - cpp2::in bitwise - ) -> void; - -#line 1117 "reflect.h2" -//----------------------------------------------------------------------- -// -// "An enum[...] is a totally ordered value type that stores a -// value of its enumerators's type, and otherwise has only public -// member variables of its enumerator's type, all of which are -// naturally scoped because they are members of a type." -// -// -- P0707R4, section 3 -// -auto cpp2_enum(meta::type_declaration& t) -> void; - -#line 1143 "reflect.h2" -//----------------------------------------------------------------------- -// -// "flag_enum expresses an enumeration that stores values -// corresponding to bitwise-or'd enumerators. The enumerators must -// be powers of two, and are automatically generated [...] A none -// value is provided [...] Operators | and & are provided to -// combine and extract values." -// -// -- P0707R4, section 3 -// -auto flag_enum(meta::type_declaration& t) -> void; - -#line 1175 "reflect.h2" -//----------------------------------------------------------------------- -// -// "As with void*, programmers should know that unions [...] are -// inherently dangerous, should be avoided wherever possible, -// and should be handled with special care when actually needed." -// -// -- Stroustrup (The Design and Evolution of C++, 14.3.4.1) -// -// "C++17 needs a type-safe union... The implications of the -// consensus `variant` design are well understood and have been -// explored over several LEWG discussions, over a thousand emails, -// a joint LEWG/EWG session, and not to mention 12 years of -// experience with Boost and other libraries." -// -// -- Axel Naumann, in P0088 (wg21.link/p0088), -// the adopted proposal for C++17 std::variant -// -//----------------------------------------------------------------------- -// -// union -// -// a type that contains exactly one of a fixed set of values at a time -// - -auto cpp2_union(meta::type_declaration& t) -> void; - -#line 1331 "reflect.h2" -//----------------------------------------------------------------------- -// -// print - output a pretty-printed visualization of t -// -auto print(cpp2::in t) -> void; - -#line 1341 "reflect.h2" -//----------------------------------------------------------------------- -// -// apply_metafunctions -// -[[nodiscard]] auto apply_metafunctions( - declaration_node& n, - type_declaration& rtype, - auto const& error - ) -> bool; - -#line 1445 "reflect.h2" -} - + [[nodiscard]] auto source_position::to_string() const& -> std::string { return "(" + cpp2::to_string(lineno) + "," + cpp2::to_string(colno) + ")"; } + + + source_position::source_position(source_position const& that) + : lineno{ that.lineno } + , colno{ that.colno }{} + +auto source_position::operator=(source_position const& that) -> source_position& { + lineno = that.lineno; + colno = that.colno; + return *this;} +source_position::source_position(source_position&& that) noexcept + : lineno{ std::move(that).lineno } + , colno{ std::move(that).colno }{} +auto source_position::operator=(source_position&& that) noexcept -> source_position& { + lineno = std::move(that).lineno; + colno = std::move(that).colno; + return *this;} +constexpr passing_style::passing_style(cpp2::in _val) + : _value{ cpp2::unsafe_narrow(_val) } { } +constexpr auto passing_style::operator=(cpp2::in _val) -> passing_style& { + _value = cpp2::unsafe_narrow(_val); + return *this; } +inline CPP2_CONSTEXPR passing_style passing_style::in = 0; + +inline CPP2_CONSTEXPR passing_style passing_style::copy = 1; + +inline CPP2_CONSTEXPR passing_style passing_style::inout = 2; + +inline CPP2_CONSTEXPR passing_style passing_style::out = 3; + +inline CPP2_CONSTEXPR passing_style passing_style::move = 4; + +inline CPP2_CONSTEXPR passing_style passing_style::forward = 5; + +inline CPP2_CONSTEXPR passing_style passing_style::invalid = 6; + +[[nodiscard]] constexpr auto passing_style::get_raw_value() const& -> cpp2::i8 { return _value; } +constexpr passing_style::passing_style() + : _value{ in._value }{} +constexpr passing_style::passing_style(passing_style const& that) + : _value{ that._value }{} +constexpr auto passing_style::operator=(passing_style const& that) -> passing_style& { + _value = that._value; + return *this;} +constexpr passing_style::passing_style(passing_style&& that) noexcept + : _value{ std::move(that)._value }{} +constexpr auto passing_style::operator=(passing_style&& that) noexcept -> passing_style& { + _value = std::move(that)._value; + return *this;} +[[nodiscard]] auto passing_style::to_string() const& -> std::string{ + if ((*this) == in) {return "in"; } + if ((*this) == copy) {return "copy"; } + if ((*this) == inout) {return "inout"; } + if ((*this) == out) {return "out"; } + if ((*this) == move) {return "move"; } + if ((*this) == forward) {return "forward"; } + if ((*this) == invalid) {return "invalid"; } + return "invalid passing_style value"; + } +#line 56 "reflect.h2" +[[nodiscard]] auto to_passing_style(cpp2::in s) -> passing_style +{ + return [&] () -> passing_style { auto&& _expr = s; + if (cpp2::is(_expr, (CPP2_UFCS(to_string)(passing_style::in)))) { if constexpr( requires{passing_style::in;} ) if constexpr( std::is_convertible_v ) return passing_style::in; else return passing_style{}; else return passing_style{}; } + else if (cpp2::is(_expr, CPP2_UFCS(to_string)(passing_style::copy))) { if constexpr( requires{passing_style::copy;} ) if constexpr( std::is_convertible_v ) return passing_style::copy; else return passing_style{}; else return passing_style{}; } + else if (cpp2::is(_expr, CPP2_UFCS(to_string)(passing_style::inout))) { if constexpr( requires{passing_style::inout;} ) if constexpr( std::is_convertible_v ) return passing_style::inout; else return passing_style{}; else return passing_style{}; } + else if (cpp2::is(_expr, CPP2_UFCS(to_string)(passing_style::out))) { if constexpr( requires{passing_style::out;} ) if constexpr( std::is_convertible_v ) return passing_style::out; else return passing_style{}; else return passing_style{}; } + else if (cpp2::is(_expr, CPP2_UFCS(to_string)(passing_style::move))) { if constexpr( requires{passing_style::move;} ) if constexpr( std::is_convertible_v ) return passing_style::move; else return passing_style{}; else return passing_style{}; } + else if (cpp2::is(_expr, CPP2_UFCS(to_string)(passing_style::forward))) { if constexpr( requires{passing_style::forward;} ) if constexpr( std::is_convertible_v ) return passing_style::forward; else return passing_style{}; else return passing_style{}; } + else return passing_style::invalid; } + (); } +#line 82 "reflect.h2" + [[nodiscard]] auto compiler_services::data() const& -> auto&& { return std::any_cast>(data_); } + [[nodiscard]] auto compiler_services::data() & -> auto&& { return std::any_cast>(data_); } -//=== Cpp2 function definitions ================================================= - -#line 1 "reflect.h2" - -#line 20 "reflect.h2" -namespace cpp2 { - -namespace meta { - -#line 46 "reflect.h2" +#line 87 "reflect.h2" compiler_services::compiler_services( - std::vector* errors_, - std::deque* generated_tokens_ + cpp2::in data_v ) - : errors{ errors_ } - , errors_original_size{ cpp2::unsafe_narrow(std::ssize(*cpp2::assert_not_null(errors))) } - , generated_tokens{ generated_tokens_ } - , parser{ *cpp2::assert_not_null(errors) } -#line 51 "reflect.h2" + : data_{ data_v } +#line 91 "reflect.h2" { -#line 56 "reflect.h2" + if (cpp2::Type.has_handler() && !(CPP2_UFCS(type)(data_) == Typeid()) ) { cpp2::Type.report_violation(CPP2_CONTRACT_MSG("parameter 'data_v' must store a 'compiler_services_data'")); } } -#line 60 "reflect.h2" +#line 98 "reflect.h2" auto compiler_services::set_metafunction_name(cpp2::in name, cpp2::in> args) & -> void{ - metafunction_name = name; - metafunction_args = args; - metafunctions_used = CPP2_UFCS(empty)(args); + data().metafunction_name = name; + data().metafunction_args = args; + data().metafunctions_used = CPP2_UFCS(empty)(args); } - [[nodiscard]] auto compiler_services::get_metafunction_name() const& -> std::string_view { return metafunction_name; } + [[nodiscard]] auto compiler_services::get_metafunction_name() const& -> std::string_view { return data().metafunction_name; } [[nodiscard]] auto compiler_services::get_argument(cpp2::in index) & -> std::string{ - metafunctions_used = true; - if (([_0 = 0, _1 = index, _2 = CPP2_UFCS(ssize)(metafunction_args)]{ return cpp2::cmp_less_eq(_0,_1) && cpp2::cmp_less(_1,_2); }())) { - return CPP2_ASSERT_IN_BOUNDS(metafunction_args, index); + data().metafunctions_used = true; + if (([_0 = 0, _1 = index, _2 = CPP2_UFCS(ssize)(data().metafunction_args)]{ return cpp2::cmp_less_eq(_0,_1) && cpp2::cmp_less(_1,_2); }())) { + return CPP2_ASSERT_IN_BOUNDS(data().metafunction_args, index); } return ""; } [[nodiscard]] auto compiler_services::get_arguments() & -> std::vector{ - metafunctions_used = true; - return metafunction_args; + data().metafunctions_used = true; + return data().metafunction_args; } - [[nodiscard]] auto compiler_services::arguments_were_used() const& -> bool { return metafunctions_used; } + [[nodiscard]] auto compiler_services::arguments_were_used() const& -> bool { return data().metafunctions_used; } [[nodiscard]] auto compiler_services::parse_statement( std::string_view source - ) & -> parse_statement_ret + ) & -> auto { - cpp2::deferred_init> ret; -#line 89 "reflect.h2" auto original_source {source}; CPP2_UFCS(push_back)(generated_lines, std::vector()); @@ -823,7 +170,7 @@ auto newline_pos = CPP2_UFCS(find)(source, '\n'); // First split this string into source_lines // -#line 101 "reflect.h2" +#line 139 "reflect.h2" if ( cpp2::cmp_greater(CPP2_UFCS(ssize)(source),1) && newline_pos != source.npos) { @@ -836,7 +183,7 @@ auto newline_pos = CPP2_UFCS(find)(source, '\n'); } } -#line 112 "reflect.h2" +#line 150 "reflect.h2" if (!(CPP2_UFCS(empty)(source))) { std::move(add_line)(std::move(source)); } @@ -844,7 +191,7 @@ auto newline_pos = CPP2_UFCS(find)(source, '\n'); // Now lex this source fragment to generate // a single grammar_map entry, whose .second // is the vector of tokens - static_cast(CPP2_UFCS(emplace_back)(generated_lexers, *cpp2::assert_not_null(errors))); + static_cast(CPP2_UFCS(emplace_back)(generated_lexers, *cpp2::assert_not_null(data().errors))); auto tokens {&CPP2_UFCS(back)(generated_lexers)}; CPP2_UFCS(lex)((*cpp2::assert_not_null(tokens)), *cpp2::assert_not_null(std::move(lines)), true); @@ -852,13 +199,14 @@ auto newline_pos = CPP2_UFCS(find)(source, '\n'); // Now parse this single declaration from // the lexed tokens - ret.construct(CPP2_UFCS(parse_one_declaration)(parser, - (*cpp2::assert_not_null(CPP2_UFCS(begin)(CPP2_UFCS(get_map)(*cpp2::assert_not_null(std::move(tokens)))))).second, - *cpp2::assert_not_null(generated_tokens) - )); - if (!(ret.value())) { + auto ret {CPP2_UFCS(parse_one_declaration)(data().parser, + (*cpp2::assert_not_null(CPP2_UFCS(begin)(CPP2_UFCS(get_map)(*cpp2::assert_not_null(std::move(tokens)))))).second, + *cpp2::assert_not_null(data().generated_tokens) + )}; + if (!(ret)) { error("parse failed - the source string is not a valid statement:\n" + cpp2::to_string(std::move(original_source))); - }return std::move(ret.value()); + } + return ret; } [[nodiscard]] auto compiler_services::position() const -> source_position @@ -867,7 +215,7 @@ auto newline_pos = CPP2_UFCS(find)(source, '\n'); return { }; } -#line 145 "reflect.h2" +#line 184 "reflect.h2" auto compiler_services::require( cpp2::in b, @@ -882,43 +230,76 @@ auto newline_pos = CPP2_UFCS(find)(source, '\n'); auto compiler_services::error(cpp2::in msg) const& -> void { auto message {cpp2::as_(msg)}; - if (!(CPP2_UFCS(empty)(metafunction_name))) { - message = "while applying @" + cpp2::to_string(metafunction_name) + " - " + cpp2::to_string(message); + if (!(CPP2_UFCS(empty)(data().metafunction_name))) { + message = "while applying @" + cpp2::to_string(data().metafunction_name) + " - " + cpp2::to_string(message); } - static_cast(CPP2_UFCS(emplace_back)((*cpp2::assert_not_null(errors)), position(), std::move(message))); + static_cast(CPP2_UFCS(emplace_back)((*cpp2::assert_not_null(data().errors)), position(), std::move(message))); } -#line 168 "reflect.h2" +#line 207 "reflect.h2" auto compiler_services::report_violation(auto const& msg) const& -> void{ error(msg); - throw(std::runtime_error(" ==> programming bug found in metafunction @" + cpp2::to_string(metafunction_name) + " - contract violation - see previous errors")); + throw(std::runtime_error(" ==> programming bug found in metafunction @" + cpp2::to_string(data().metafunction_name) + " - contract violation - see previous errors")); } [[nodiscard]] auto compiler_services::has_handler() const& -> auto { return true; } compiler_services::~compiler_services() noexcept{} compiler_services::compiler_services(compiler_services const& that) - : errors{ that.errors } - , errors_original_size{ that.errors_original_size } - , generated_tokens{ that.generated_tokens } - , parser{ that.parser } - , metafunction_name{ that.metafunction_name } - , metafunction_args{ that.metafunction_args } - , metafunctions_used{ that.metafunctions_used }{} - -#line 229 "reflect.h2" + : data_{ that.data_ }{} + +#line 270 "reflect.h2" + template declaration_base::node_pointer::node_pointer( + + T const& n_ + ) + : n{ n_ } +#line 274 "reflect.h2" + { + + if (cpp2::Default.has_handler() && !(n_) ) { cpp2::Default.report_violation(CPP2_CONTRACT_MSG("a meta::declaration must point to a valid declaration_node, not null")); } + static_assert(std::is_same_v); + } +#line 270 "reflect.h2" + template auto declaration_base::node_pointer::operator=( + + T const& n_ + ) -> node_pointer& + { + n = n_; + +#line 276 "reflect.h2" + if (cpp2::Default.has_handler() && !(n_) ) { cpp2::Default.report_violation(CPP2_CONTRACT_MSG("a meta::declaration must point to a valid declaration_node, not null")); } + static_assert(std::is_same_v); + return *this; +#line 278 "reflect.h2" + } + + [[nodiscard]] auto declaration_base::node_pointer::operator*() const& -> auto&& { return *cpp2::assert_not_null(unsafe_narrow(n)); } + + declaration_base::node_pointer::node_pointer(node_pointer const& that) + : n{ that.n }{} + +auto declaration_base::node_pointer::operator=(node_pointer const& that) -> node_pointer& { + n = that.n; + return *this;} +declaration_base::node_pointer::node_pointer(node_pointer&& that) noexcept + : n{ std::move(that).n }{} +auto declaration_base::node_pointer::operator=(node_pointer&& that) noexcept -> node_pointer& { + n = std::move(that).n; + return *this;} +#line 285 "reflect.h2" declaration_base::declaration_base( - declaration_node* n_, + cpp2::in n_, cpp2::in s ) : compiler_services{ s } , n{ n_ } -#line 234 "reflect.h2" +#line 290 "reflect.h2" { -#line 237 "reflect.h2" - if (cpp2::Default.has_handler() && !(n) ) { cpp2::Default.report_violation(CPP2_CONTRACT_MSG("a meta::declaration must point to a valid declaration_node, not null")); } +#line 293 "reflect.h2" } [[nodiscard]] auto declaration_base::position() const -> source_position { return CPP2_UFCS(position)((*cpp2::assert_not_null(n))); } @@ -930,14 +311,14 @@ declaration_base::declaration_base(declaration_base const& that) : compiler_services{ static_cast(that) } , n{ that.n }{} -#line 253 "reflect.h2" +#line 308 "reflect.h2" declaration::declaration( - declaration_node* n_, + cpp2::in n_, cpp2::in s ) : declaration_base{ n_, s } -#line 258 "reflect.h2" +#line 313 "reflect.h2" { } @@ -955,6 +336,9 @@ declaration_base::declaration_base(declaration_base const& that) [[nodiscard]] auto declaration::make_protected() & -> bool { return CPP2_UFCS(make_protected)((*cpp2::assert_not_null(n))); } [[nodiscard]] auto declaration::make_private() & -> bool { return CPP2_UFCS(make_private)((*cpp2::assert_not_null(n))); } + [[nodiscard]] auto declaration::is_dll_visible() const& -> bool { return CPP2_UFCS(is_dll_visible)((*cpp2::assert_not_null(n))); } + [[nodiscard]] auto declaration::make_dll_visible() & -> bool { return CPP2_UFCS(make_dll_visible)((*cpp2::assert_not_null(n))); } + [[nodiscard]] auto declaration::has_name() const& -> bool { return CPP2_UFCS(has_name)((*cpp2::assert_not_null(n))); } [[nodiscard]] auto declaration::has_name(cpp2::in s) const& -> bool { return CPP2_UFCS(has_name)((*cpp2::assert_not_null(n)), s); } @@ -1003,7 +387,7 @@ declaration_base::declaration_base(declaration_base const& that) { if (cpp2::Type.has_handler() && !(parent_is_type()) ) { cpp2::Type.report_violation(""); } -#line 322 "reflect.h2" +#line 380 "reflect.h2" auto test {CPP2_UFCS(type_member_mark_for_removal)((*cpp2::assert_not_null(n)))}; if (cpp2::Default.has_handler() && !(std::move(test)) ) { cpp2::Default.report_violation(""); }// ... to ensure this assert is true } @@ -1012,14 +396,14 @@ declaration_base::declaration_base(declaration_base const& that) declaration::declaration(declaration const& that) : declaration_base{ static_cast(that) }{} -#line 335 "reflect.h2" +#line 393 "reflect.h2" function_declaration::function_declaration( - declaration_node* n_, + cpp2::in n_, cpp2::in s ) : declaration{ n_, s } -#line 340 "reflect.h2" +#line 398 "reflect.h2" { if (cpp2::Default.has_handler() && !(CPP2_UFCS(is_function)((*cpp2::assert_not_null(n)))) ) { cpp2::Default.report_violation(""); } @@ -1077,16 +461,16 @@ declaration::declaration(declaration const& that) auto function_declaration::add_initializer(cpp2::in source) & -> void -#line 398 "reflect.h2" +#line 456 "reflect.h2" { if ((*this).has_handler() && !(!(has_initializer())) ) { (*this).report_violation(CPP2_CONTRACT_MSG("cannot add an initializer to a function that already has one")); } if ((*this).has_handler() && !(parent_is_type()) ) { (*this).report_violation(CPP2_CONTRACT_MSG("cannot add an initializer to a function that isn't in a type scope")); } - //require( !has_initializer(), + //require( !has_initializer(), // "cannot add an initializer to a function that already has one"); //require( parent_is_type(), // "cannot add an initializer to a function that isn't in a type scope"); -#line 404 "reflect.h2" +#line 462 "reflect.h2" auto stmt {parse_statement(source)}; if (!((cpp2::as_(stmt)))) { error("cannot add an initializer that is not a valid statement"); @@ -1099,14 +483,14 @@ declaration::declaration(declaration const& that) function_declaration::function_declaration(function_declaration const& that) : declaration{ static_cast(that) }{} -#line 422 "reflect.h2" +#line 480 "reflect.h2" object_declaration::object_declaration( - declaration_node* n_, + cpp2::in n_, cpp2::in s ) : declaration{ n_, s } -#line 427 "reflect.h2" +#line 485 "reflect.h2" { if (cpp2::Default.has_handler() && !(CPP2_UFCS(is_object)((*cpp2::assert_not_null(n)))) ) { cpp2::Default.report_violation(""); } @@ -1132,14 +516,14 @@ declaration::declaration(declaration const& that) object_declaration::object_declaration(object_declaration const& that) : declaration{ static_cast(that) }{} -#line 458 "reflect.h2" +#line 516 "reflect.h2" type_declaration::type_declaration( - declaration_node* n_, + cpp2::in n_, cpp2::in s ) : declaration{ n_, s } -#line 463 "reflect.h2" +#line 521 "reflect.h2" { if (cpp2::Default.has_handler() && !(CPP2_UFCS(is_type)((*cpp2::assert_not_null(n)))) ) { cpp2::Default.report_violation(""); } @@ -1229,13 +613,13 @@ declaration::declaration(declaration const& that) [[nodiscard]] auto type_declaration::query_declared_value_set_functions() const& -> query_declared_value_set_functions_ret -#line 557 "reflect.h2" +#line 615 "reflect.h2" { cpp2::deferred_init out_this_in_that; cpp2::deferred_init out_this_move_that; cpp2::deferred_init inout_this_in_that; cpp2::deferred_init inout_this_move_that; -#line 558 "reflect.h2" +#line 616 "reflect.h2" auto declared {CPP2_UFCS(find_declared_value_set_functions)((*cpp2::assert_not_null(n)))}; out_this_in_that.construct(declared.out_this_in_that != nullptr); out_this_move_that.construct(declared.out_this_move_that != nullptr); @@ -1265,14 +649,14 @@ declaration::declaration(declaration const& that) type_declaration::type_declaration(type_declaration const& that) : declaration{ static_cast(that) }{} -#line 593 "reflect.h2" +#line 651 "reflect.h2" alias_declaration::alias_declaration( - declaration_node* n_, + cpp2::in n_, cpp2::in s ) : declaration{ n_, s } -#line 598 "reflect.h2" +#line 656 "reflect.h2" { if (cpp2::Default.has_handler() && !(CPP2_UFCS(is_alias)((*cpp2::assert_not_null(n)))) ) { cpp2::Default.report_violation(""); } @@ -1281,14 +665,14 @@ declaration::declaration(declaration const& that) alias_declaration::alias_declaration(alias_declaration const& that) : declaration{ static_cast(that) }{} -#line 617 "reflect.h2" +#line 675 "reflect.h2" auto add_virtual_destructor(meta::type_declaration& t) -> void { CPP2_UFCS(add_member)(t, "operator=: (virtual move this) = { }"); } -#line 635 "reflect.h2" -auto interface(meta::type_declaration& t) -> void +#line 693 "reflect.h2" +CPPFRONTAPI auto interface(meta::type_declaration& t) -> void { auto has_dtor {false}; @@ -1314,8 +698,8 @@ auto interface(meta::type_declaration& t) -> void } } -#line 681 "reflect.h2" -auto polymorphic_base(meta::type_declaration& t) -> void +#line 739 "reflect.h2" +CPPFRONTAPI auto polymorphic_base(meta::type_declaration& t) -> void { auto has_dtor {false}; @@ -1339,7 +723,7 @@ auto polymorphic_base(meta::type_declaration& t) -> void } } -#line 726 "reflect.h2" +#line 784 "reflect.h2" auto ordered_impl( meta::type_declaration& t, cpp2::in ordering @@ -1364,26 +748,26 @@ auto ordered_impl( } } -#line 755 "reflect.h2" -auto ordered(meta::type_declaration& t) -> void +#line 813 "reflect.h2" +CPPFRONTAPI auto ordered(meta::type_declaration& t) -> void { ordered_impl(t, "strong_ordering"); } -#line 763 "reflect.h2" -auto weakly_ordered(meta::type_declaration& t) -> void +#line 821 "reflect.h2" +CPPFRONTAPI auto weakly_ordered(meta::type_declaration& t) -> void { ordered_impl(t, "weak_ordering"); } -#line 771 "reflect.h2" -auto partially_ordered(meta::type_declaration& t) -> void +#line 829 "reflect.h2" +CPPFRONTAPI auto partially_ordered(meta::type_declaration& t) -> void { ordered_impl(t, "partial_ordering"); } -#line 793 "reflect.h2" -auto copyable(meta::type_declaration& t) -> void +#line 851 "reflect.h2" +CPPFRONTAPI auto copyable(meta::type_declaration& t) -> void { // If the user explicitly wrote any of the copy/move functions, // they must also have written the most general one - we can't @@ -1404,8 +788,8 @@ auto copyable(meta::type_declaration& t) -> void }} } -#line 821 "reflect.h2" -auto basic_value(meta::type_declaration& t) -> void +#line 879 "reflect.h2" +CPPFRONTAPI auto basic_value(meta::type_declaration& t) -> void { CPP2_UFCS(copyable)(t); @@ -1423,27 +807,27 @@ auto basic_value(meta::type_declaration& t) -> void } } -#line 849 "reflect.h2" -auto value(meta::type_declaration& t) -> void +#line 907 "reflect.h2" +CPPFRONTAPI auto value(meta::type_declaration& t) -> void { CPP2_UFCS(ordered)(t); CPP2_UFCS(basic_value)(t); } -auto weakly_ordered_value(meta::type_declaration& t) -> void +CPPFRONTAPI auto weakly_ordered_value(meta::type_declaration& t) -> void { CPP2_UFCS(weakly_ordered)(t); CPP2_UFCS(basic_value)(t); } -auto partially_ordered_value(meta::type_declaration& t) -> void +CPPFRONTAPI auto partially_ordered_value(meta::type_declaration& t) -> void { CPP2_UFCS(partially_ordered)(t); CPP2_UFCS(basic_value)(t); } -#line 893 "reflect.h2" -auto cpp2_struct(meta::type_declaration& t) -> void +#line 951 "reflect.h2" +CPPFRONTAPI auto cpp2_struct(meta::type_declaration& t) -> void { for ( auto& m : CPP2_UFCS(get_members)(t) ) { @@ -1460,7 +844,7 @@ auto cpp2_struct(meta::type_declaration& t) -> void CPP2_UFCS(disable_member_function_generation)(t); } -#line 934 "reflect.h2" +#line 992 "reflect.h2" auto basic_enum( meta::type_declaration& t, auto const& nextval, @@ -1485,7 +869,7 @@ auto basic_enum( { std::string value = "-1"; -#line 957 "reflect.h2" +#line 1015 "reflect.h2" for ( auto const& m : CPP2_UFCS(get_members)(t) ) if ( CPP2_UFCS(is_member_object)(m)) @@ -1523,7 +907,7 @@ std::string value = "-1"; } } -#line 993 "reflect.h2" +#line 1051 "reflect.h2" if ((CPP2_UFCS(empty)(enumerators))) { CPP2_UFCS(error)(t, "an enumeration must contain at least one enumerator value"); return ; @@ -1569,7 +953,7 @@ std::string value = "-1"; } } -#line 1039 "reflect.h2" +#line 1097 "reflect.h2" // 2. Replace: Erase the contents and replace with modified contents // // Note that most values and functions are declared as '==' compile-time values, i.e. Cpp1 'constexpr' @@ -1617,7 +1001,7 @@ std::string to_string = " to_string: (this) -> std::string = { \n"; // Provide a 'to_string' function to print enumerator name(s) -#line 1084 "reflect.h2" +#line 1142 "reflect.h2" { if (bitwise) { to_string += " _ret : std::string = \"(\";\n"; @@ -1649,11 +1033,11 @@ std::string to_string = " to_string: (this) -> std::string = { \n"; CPP2_UFCS(add_member)(t, std::move(to_string)); } } -#line 1114 "reflect.h2" +#line 1172 "reflect.h2" } -#line 1126 "reflect.h2" -auto cpp2_enum(meta::type_declaration& t) -> void +#line 1184 "reflect.h2" +CPPFRONTAPI auto cpp2_enum(meta::type_declaration& t) -> void { // Let basic_enum do its thing, with an incrementing value generator CPP2_UFCS(basic_enum)(t, @@ -1669,8 +1053,8 @@ auto cpp2_enum(meta::type_declaration& t) -> void ); } -#line 1153 "reflect.h2" -auto flag_enum(meta::type_declaration& t) -> void +#line 1211 "reflect.h2" +CPPFRONTAPI auto flag_enum(meta::type_declaration& t) -> void { // Let basic_enum do its thing, with a power-of-two value generator CPP2_UFCS(basic_enum)(t, @@ -1691,8 +1075,8 @@ auto flag_enum(meta::type_declaration& t) -> void ); } -#line 1199 "reflect.h2" -auto cpp2_union(meta::type_declaration& t) -> void +#line 1257 "reflect.h2" +CPPFRONTAPI auto cpp2_union(meta::type_declaration& t) -> void { std::vector alternatives {}; { @@ -1700,7 +1084,7 @@ auto value = 0; // 1. Gather: All the user-written members, and find/compute the max size -#line 1206 "reflect.h2" +#line 1264 "reflect.h2" for ( auto const& m : CPP2_UFCS(get_members)(t) ) { do @@ -1725,7 +1109,7 @@ auto value = 0; } while (false); ++value; } } -#line 1229 "reflect.h2" +#line 1287 "reflect.h2" std::string discriminator_type {}; if (cpp2::cmp_less(CPP2_UFCS(ssize)(alternatives),std::numeric_limits::max())) { discriminator_type = "i8"; @@ -1740,7 +1124,7 @@ auto value = 0; discriminator_type = "i64"; }}} -#line 1244 "reflect.h2" +#line 1302 "reflect.h2" // 2. Replace: Erase the contents and replace with modified contents CPP2_UFCS(remove_marked_members)(t); @@ -1749,12 +1133,12 @@ std::string storage = " _storage: cpp2::aligned_storage t) -> void +#line 1393 "reflect.h2" +CPPFRONTAPI auto print(cpp2::in t) -> void { std::cout << CPP2_UFCS(print)(t) << "\n"; } -#line 1345 "reflect.h2" -[[nodiscard]] auto apply_metafunctions( - declaration_node& n, - type_declaration& rtype, - auto const& error - ) -> bool - +#line 1403 "reflect.h2" +CPPFRONTAPI auto dll_visible(meta::type_declaration& t) -> void { - if (cpp2::Default.has_handler() && !(CPP2_UFCS(is_type)(n)) ) { cpp2::Default.report_violation(""); } - - // Check for _names reserved for the metafunction implementation - for ( - auto const& m : CPP2_UFCS(get_members)(rtype) ) - { - CPP2_UFCS(require)(m, !(CPP2_UFCS(starts_with)(CPP2_UFCS(name)(m), "_")) || cpp2::cmp_greater(CPP2_UFCS(ssize)(CPP2_UFCS(name)(m)),1), - "a type that applies a metafunction cannot have a body that declares a name that starts with '_' - those names are reserved for the metafunction implementation"); - } - - // For each metafunction, apply it - for ( - auto const& meta : n.metafunctions ) - { - // Convert the name and any template arguments to strings - // and record that in rtype - auto name {CPP2_UFCS(to_string)((*cpp2::assert_not_null(meta)))}; - name = CPP2_UFCS(substr)(name, 0, CPP2_UFCS(find)(name, '<')); - - std::vector args {}; - for ( - auto const& arg : CPP2_UFCS(template_arguments)((*cpp2::assert_not_null(meta))) ) - CPP2_UFCS(push_back)(args, CPP2_UFCS(to_string)(arg)); - - CPP2_UFCS(set_metafunction_name)(rtype, name, args); - - // Dispatch - // - if (name == "interface") { - interface(rtype); - } - else {if (name == "polymorphic_base") { - polymorphic_base(rtype); - } - else {if (name == "ordered") { - ordered(rtype); - } - else {if (name == "weakly_ordered") { - weakly_ordered(rtype); - } - else {if (name == "partially_ordered") { - partially_ordered(rtype); - } - else {if (name == "copyable") { - copyable(rtype); - } - else {if (name == "basic_value") { - basic_value(rtype); - } - else {if (name == "value") { - value(rtype); - } - else {if (name == "weakly_ordered_value") { - weakly_ordered_value(rtype); - } - else {if (name == "partially_ordered_value") { - partially_ordered_value(rtype); - } - else {if (name == "struct") { - cpp2_struct(rtype); - } - else {if (name == "enum") { - cpp2_enum(rtype); - } - else {if (name == "flag_enum") { - flag_enum(rtype); - } - else {if (name == "union") { - cpp2_union(rtype); - } - else {if (name == "print") { - print(rtype); - } - else { - error("unrecognized metafunction name: " + name); - error("(temporary alpha limitation) currently the supported names are: interface, polymorphic_base, ordered, weakly_ordered, partially_ordered, copyable, basic_value, value, weakly_ordered_value, partially_ordered_value, struct, enum, flag_enum, union, print"); - return false; - }}}}}}}}}}}}}}} - - if (( - !(CPP2_UFCS(empty)(args)) - && !(CPP2_UFCS(arguments_were_used)(rtype)))) - - { - error(name + " did not use its template arguments - did you mean to write '" + name + " <" + CPP2_ASSERT_IN_BOUNDS(args, 0) + "> type' (with the spaces)?"); - return false; - } - } - - return true; + CPP2_UFCS(require)(t, CPP2_UFCS(make_dll_visible)(t), + "dll_visible can only be applied to a namespace-scope name"); +} +CPPFRONTAPI auto dll_visible(meta::function_declaration& t) -> void +{ + CPP2_UFCS(require)(t, CPP2_UFCS(make_dll_visible)(t), + "dll_visible can only be applied to a namespace-scope name"); } -#line 1445 "reflect.h2" +#line 1415 "reflect.h2" } } diff --git a/source/lex.h b/source/lex.h index 478fe3da8..36bcf9f64 100644 --- a/source/lex.h +++ b/source/lex.h @@ -375,7 +375,7 @@ auto expand_string_literal( ++pos; auto current_start = pos; // the current offset before which the string has been added to ret - auto parts = string_parts{std::string(text.substr(0, current_start)), // begin sequence ", U", u8" depends on the string type + auto parts = string_parts{std::string(text.substr(0, current_start)), // begin sequence ", U", u8" depends on the string type "\"", // end sequence string_parts::on_both_ends}; // add opening and closing sequence to generated string @@ -1059,8 +1059,8 @@ auto lex_line( const std::string& opening_seq, const std::string& closing_seq, string_parts::adds_sequences closing_strategy, - std::string_view part, - int pos_to_replace, + std::string_view part, + int pos_to_replace, int size_to_replace ) -> bool { auto parts = expand_raw_string_literal(opening_seq, closing_seq, closing_strategy, part, errors, source_position(lineno, pos_to_replace + 1)); @@ -1094,7 +1094,7 @@ auto lex_line( auto peek3 = peek(3); //G encoding-prefix: one of - //G 'u8' 'u' 'uR' 'u8R' 'U' 'UR' 'L' 'LR' 'R' + //G 'u8' 'u' 'uR' 'u8R' 'U' 'UR' 'L' 'LR' 'R' //G auto is_encoding_prefix_and = [&](char next) { if (line[i] == next) { return 1; } // " @@ -1103,15 +1103,15 @@ auto lex_line( else if (peek1 == '8' && peek2 == next) { return 3; } // u8" else if (peek1 == 'R' && peek2 == next) { return 3; } // uR" else if (peek1 == '8' && peek2 == 'R' && peek3 == next) { return 4; } // u8R" - } - else if (line[i] == 'U') { + } + else if (line[i] == 'U') { if ( peek1 == next) { return 2; } // U" else if (peek1 == 'R' && peek2 == next) { return 3; } // UR" - } - else if (line[i] == 'L') { + } + else if (line[i] == 'L') { if ( peek1 == next ) { return 2; } // L" - else if (peek1 == 'R' && peek2 == next) { return 3; } // LR" - } + else if (peek1 == 'R' && peek2 == next) { return 3; } // LR" + } else if (line[i] == 'R' && peek1 == next) { return 2; } // R" return 0; }; @@ -1143,7 +1143,7 @@ auto lex_line( auto part = line.substr(i, end_pos-i); if (const auto& rsm = raw_string_multiline.value(); rsm.should_interpolate) { - + auto closing_strategy = end_pos == line.npos ? string_parts::no_ends : string_parts::on_the_end; auto size_to_replace = end_pos == line.npos ? std::ssize(line) - i : end_pos - i + std::ssize(rsm.closing_seq); @@ -1369,7 +1369,7 @@ auto lex_line( // if peek(j-2) is 'R' it means that we deal with raw-string literal auto R_pos = i + 1; auto seq_pos = i + 3; - + if (auto paren_pos = line.find("(", seq_pos); paren_pos != std::string::npos) { auto opening_seq = line.substr(i, paren_pos - i + 1); auto closing_seq = ")"+line.substr(seq_pos, paren_pos-seq_pos)+"\""; @@ -1582,9 +1582,9 @@ auto lex_line( //G else if (auto j = is_encoding_prefix_and('\"')) { // if peek(j-2) is 'R' it means that we deal with raw-string literal - if (peek(j-2) == 'R') { + if (peek(j-2) == 'R') { auto seq_pos = i + j; - + if (auto paren_pos = line.find("(", seq_pos); paren_pos != std::string::npos) { auto opening_seq = line.substr(i, paren_pos - i + 1); auto closing_seq = ")"+line.substr(seq_pos, paren_pos-seq_pos)+"\""; diff --git a/source/meta_lib_impl.cpp b/source/meta_lib_impl.cpp new file mode 100644 index 000000000..06b33ad9a --- /dev/null +++ b/source/meta_lib_impl.cpp @@ -0,0 +1,28 @@ +#include "cpp2util.h" +#include + +namespace cpp2::meta { + +static std::vector registry; + +register_function::register_function(const char* name, mf_signature_in* f) { + std::cout << "\nRegistering function (in) -> " << name << "..."; + registry.emplace_back(record{name, f}); +} + +register_function::register_function(const char* name, mf_signature_inout* f) { + std::cout << "\nRegistering function (inout) -> " << name << "..."; + registry.emplace_back(record{name, f}); +} + +register_function::~register_function() { + std::cout << "\nCalling register_function destructor"; +} + +} + +void* cpp2_meta_registry_(size_t* registry_size) { + *registry_size = ::cpp2::meta::registry.size(); + return static_cast(::cpp2::meta::registry.data()); +} +CPP2_C_API constexpr auto cpp2_meta_registry = &cpp2_meta_registry_; diff --git a/source/parse.h b/source/parse.h index cf77227f0..1ece01800 100644 --- a/source/parse.h +++ b/source/parse.h @@ -22,6 +22,7 @@ #include #include #include +#include namespace cpp2 { @@ -594,29 +595,8 @@ auto binary_expression_node::is_standalone_expression() const } -enum class passing_style { in=0, copy, inout, out, move, forward, invalid }; -auto to_passing_style(token const& t) -> passing_style { - if (t.type() == lexeme::Identifier) { - if (t == "in" ) { return passing_style::in; } - if (t == "copy" ) { return passing_style::copy; } - if (t == "inout" ) { return passing_style::inout; } - if (t == "out" ) { return passing_style::out; } - if (t == "move" ) { return passing_style::move; } - if (t == "forward") { return passing_style::forward; } - } - return passing_style::invalid; -} -auto to_string_view(passing_style pass) -> std::string_view { - switch (pass) { - break;case passing_style::in : return "in"; - break;case passing_style::copy : return "copy"; - break;case passing_style::inout : return "inout"; - break;case passing_style::out : return "out"; - break;case passing_style::move : return "move"; - break;case passing_style::forward: return "forward"; - break;default : return "INVALID passing_style"; - } -} +using meta::passing_style; +using meta::to_passing_style; struct expression_list_node @@ -626,7 +606,7 @@ struct expression_list_node bool inside_initializer = false; struct term { - passing_style pass = {}; + passing_style pass = passing_style{}; std::unique_ptr expr; auto visit(auto& v, int depth) -> void @@ -2261,6 +2241,12 @@ struct function_type_node auto is_destructor() const -> bool; + auto is_metafunction() const + -> bool; + + auto is_const_metafunction() const + -> bool; + auto has_declared_return_type() const -> bool { @@ -2554,6 +2540,7 @@ struct declaration_node source_position pos; bool is_variadic = false; bool is_constexpr = false; + bool is_dll_visible_ = false; bool terse_no_equals = false; std::unique_ptr identifier; accessibility access = accessibility::default_; @@ -2568,6 +2555,7 @@ struct declaration_node > type; std::vector> metafunctions; + std::vector metafunction_lookup_checks; std::unique_ptr template_parameters; source_position requires_pos = {}; std::unique_ptr requires_clause_expression; @@ -2727,6 +2715,21 @@ struct declaration_node return set_access( accessibility::private_ ); } + auto is_dll_visible() const + -> bool + { + return is_dll_visible_; + } + + auto make_dll_visible() + -> bool + { + if (!parent_is_namespace()) { + return false; + } + return is_dll_visible_ = true; + } + auto has_name() const -> bool { @@ -2755,6 +2758,23 @@ struct declaration_node ; } + auto fully_qualified_name() const + -> std::string + { + if (!has_name()) { + return {}; + } + + auto res = std::string{}; + for (auto n = this; n; n = n->parent_declaration) + { + assert(n->identifier); + res.insert(0, n->identifier->to_string()); + res.insert(0, "::"); + } + return res; + } + auto has_initializer() const -> bool { @@ -3481,6 +3501,26 @@ struct declaration_node return ""; } + auto is_metafunction() const + -> bool + { + if (auto func = std::get_if(&type)) { + return (*func)->is_metafunction(); + } + // else + return false; + } + + auto is_const_metafunction() const + -> bool + { + if (auto func = std::get_if(&type)) { + return (*func)->is_const_metafunction(); + } + // else + return false; + } + auto is_binary_comparison_function() const -> bool { @@ -3942,6 +3982,32 @@ auto function_type_node::is_destructor() const } +auto function_type_node::is_metafunction() const + -> bool +{ + if ( + (*parameters).ssize() == 1 + && this->nth_parameter_type_name(1) == "cpp2::meta::type_declaration" + && ( + (*parameters)[0]->direction() == passing_style::in + || (*parameters)[0]->direction() == passing_style::inout + ) + ) + { + return true; + } + return false; +} + + +auto function_type_node::is_const_metafunction() const + -> bool +{ + return is_metafunction() + && (*parameters)[0]->direction() == passing_style::in; +} + + auto primary_expression_node::template_arguments() const -> std::vector const& { @@ -4196,6 +4262,7 @@ auto parameter_declaration_node::visit(auto& v, int depth) struct translation_unit_node { + bool has_interface = false; std::vector< std::unique_ptr > declarations; auto position() const -> source_position @@ -4431,7 +4498,7 @@ auto pretty_print_visualize(expression_list_node const& n, int indent) || expr.pass == passing_style::forward ) { - ret += to_string_view(expr.pass) + std::string{" "}; + ret += expr.pass.to_string() + std::string{" "}; } ret += pretty_print_visualize(*expr.expr, indent); if (++i < std::ssize(n.expressions)) { @@ -4852,7 +4919,7 @@ auto pretty_print_visualize(parameter_declaration_node const& n, int indent, boo break;default: ; // none } - ret += to_string_view(n.pass); + ret += n.pass.to_string(); ret += " "; } @@ -4906,7 +4973,7 @@ auto pretty_print_visualize(function_type_node const& n, int indent) ret += try_pretty_print_visualize(n.returns, indent+1); if (n.returns.index() == function_type_node::id) { auto& single = std::get(n.returns); - ret += to_string_view(single.pass) + ret += single.pass.to_string() + std::string{" "} + pretty_print_visualize(*single.type, indent+1); } } @@ -5122,6 +5189,118 @@ auto pretty_print_visualize(translation_unit_node const& n) } +// Consider moving these `stack` functions to `common.h` to enable more general use. + +template +auto stack_value( + T& var, + std::type_identity_t const& value +) + -> auto +{ + return finally([&var, old = std::exchange(var, value)]() { + var = old; + }); +} + +template +auto stack_element( + std::vector& cont, + std::type_identity_t const& value +) + -> auto +{ + cont.push_back(value); + return finally([&]{ cont.pop_back(); }); +} + +template +auto stack_size(std::vector& cont) + -> auto +{ + return finally([&, size = cont.size()]{ cont.resize(size); }); +} + +template +auto stack_size_if( + std::vector& cont, + bool cond +) + -> std::optional +{ + if (cond) { + return stack_size(cont); + } + return {}; +} + + +struct active_using_declaration { + qualified_id_node const* id = {}; + + explicit active_using_declaration(using_statement_node const& n) { + if (auto id_ = get_if(&n.id->id)) { + id = &**id_; + } + } + + auto introduced_identifier() const + -> std::string_view + { + if (id) { + return *id->ids.back().id->identifier; + } + // else + return {}; + } + + auto to_string() const + -> std::string + { + if (id) { + return id->to_string(); + } + // else + return {}; + } +}; + +using source_order_name_lookup_res = + std::optional>; + +using current_names_span = std::span; + +auto source_order_name_lookup(current_names_span current_names, std::string_view id) + -> source_order_name_lookup_res +{ + for ( + auto first = current_names.rbegin(), last = current_names.rend() - 1; + first != last; + ++first + ) + { + if ( + auto decl = get_if(&*first); + decl + && *decl + && (*decl)->has_name(id) + ) + { + return *decl; + } + else if ( + auto using_ = get_if(&*first); + using_ + && using_->introduced_identifier() == id + ) + { + return *using_; + } + } + + return {}; +} + //----------------------------------------------------------------------- // // parser: parses a section of Cpp2 code @@ -5155,8 +5334,12 @@ class parser } }; - // Keep a stack of currently active declarations (still being parsed) - std::vector current_declarations = { nullptr }; + // Stack of the currently active nested declarations we're inside (still being parsed) + std::vector current_declarations = { {} }; + + // Stack of the currently active names for source order name lookup: + // Like 'current_declarations' + also parameters and using declarations + std::vector current_names = { {} }; struct current_declarations_stack_guard { @@ -5254,10 +5437,12 @@ class parser // // errors error list // - parser( std::vector& errors_ ) + parser( std::vector& errors_, bool translation_unit_has_interface_ ) : errors{ errors_ } , parse_tree{std::make_unique()} - { } + { + parse_tree->has_interface = translation_unit_has_interface_; + } parser( parser const& that ) : errors{ that.errors } @@ -5265,6 +5450,9 @@ class parser { } + auto translation_unit_has_interface() -> bool { return parse_tree->has_interface; } + + //----------------------------------------------------------------------- // parse // @@ -7238,6 +7426,10 @@ class parser } next(); + if (!n->for_namespace) { + current_names.push_back(active_using_declaration{*n}); + } + return n; } @@ -7274,6 +7466,8 @@ class parser auto n = std::make_unique(compound_parent); + auto guard_parameters = stack_size_if(current_names, /*bool(n->parameters)*/ true); + // If a parameter list is allowed here, try to parse one if (parameters_allowed) { n->parameters = parameter_declaration_list(false, true, false, true); @@ -7291,6 +7485,9 @@ class parser } } } + if (!n->parameters) { + guard_parameters.reset(); + } // Now handle the rest of the statement @@ -7587,19 +7784,23 @@ class parser && n->pass != passing_style::inout ) { - switch (n->pass) { - break;case passing_style::in: + if (n->pass == passing_style::in) { error( "an 'in' parameter is always const, 'const' isn't needed and isn't allowed", false ); - break;case passing_style::inout: + } + else if (n->pass == passing_style::inout) { // error( "an 'inout' parameter can't be const, if you do want it to be const then use 'in' instead", false ); - break;case passing_style::out: + } + else if (n->pass == passing_style::out) { error( "an 'out' parameter can't be const, otherwise it can't be initialized in the function body", false ); - break;case passing_style::move: + } + else if (n->pass == passing_style::move) { error( "a 'move' parameter can't be const, otherwise it can't be moved from in the function body", false ); - break;case passing_style::forward: + } + else if (n->pass == passing_style::forward) { error( "a 'forward' parameter shouldn't be const, because it passes along the argument's actual const-ness (and actual value category)", false ); - break;default: - assert (!"ICE: missing case"); + } + else { + assert (false && "ICE: missing case"); } return {}; } @@ -7625,6 +7826,8 @@ class parser false, tok->position()); } } + + current_names.push_back(&*n->declaration); return n; } @@ -7911,7 +8114,7 @@ class parser } else { auto msg = std::string("'"); - msg += to_string_view(pass); + msg += pass.to_string(); error(msg + "' must be followed by a type-id"); } } @@ -7974,6 +8177,8 @@ class parser auto apply_type_metafunctions( declaration_node& decl ) -> bool; + auto apply_function_metafunctions( declaration_node& decl ) + -> bool; //G unnamed-declaration: @@ -8146,6 +8351,9 @@ class parser auto guard2 = current_declarations_stack_guard(this, n.get()); + current_names.push_back(&*n); + auto guard_function = stack_size_if(current_names, /*n->is_function()*/ true); + // Next is an an optional type auto deduced_type = false; @@ -8178,18 +8386,16 @@ class parser } // Or a function type, declaring a function - and tell the function whether it's in a user-defined type - else if (auto t = function_type(n.get(), named)) + else if (auto t = function_type(n.get(), named); + t + || ( + guard_function.reset(), + false + ) + ) { n->type = std::move(t); assert (n->is_function()); - - if (!n->metafunctions.empty()) { - errors.emplace_back( - n->metafunctions.front()->position(), - "(temporary alpha limitation) metafunctions are currently not supported on functions, only on types" - ); - return {}; - } } // Or a namespace @@ -8428,6 +8634,16 @@ class parser return {}; } } + // If this is a function with metafunctions, apply those + else if (n->is_function()) { + if (!apply_function_metafunctions(*n)) { + error( + "error encountered while applying function metafunctions", + false, {}, true + ); + return {}; + } + } if ( n->is_function() @@ -9105,7 +9321,7 @@ class parse_tree_printer : printing_visitor if (n.returns.index() == function_type_node::id) { auto& r = std::get(n.returns); if (r.pass != passing_style::invalid) { - o << pre(indent+1) << "returns by: " << to_string_view(r.pass) << "\n"; + o << pre(indent+1) << "returns by: " << r.pass.to_string() << "\n"; } } } @@ -9178,14 +9394,8 @@ class parse_tree_printer : printing_visitor o << pre(indent) << "parameter-declaration\n"; o << pre(indent+1); - switch (n.pass) { - break;case passing_style::in : o << "in"; - break;case passing_style::copy : o << "copy"; - break;case passing_style::inout : o << "inout"; - break;case passing_style::out : o << "out"; - break;case passing_style::move : o << "move"; - break;case passing_style::forward: o << "forward"; - break;default: ; + if (n.pass != passing_style::invalid) { + o << n.pass.to_string(); } o << pre(indent+1); diff --git a/source/reflect.h2 b/source/reflect.h2 index 072087e9a..c1287d3cb 100644 --- a/source/reflect.h2 +++ b/source/reflect.h2 @@ -15,13 +15,58 @@ // Reflection and meta //=========================================================================== -#include "parse.h" cpp2: namespace = { meta: namespace = { +lineno_t: type == std::int32_t; +colno_t: type == std::int32_t; // not int16_t... encountered >80,000 char line during testing + +source_position: @dll_visible @value type = +{ + public lineno: lineno_t = 1; // one-based offset into program source + public colno: colno_t = 1; // one-based offset into line + + operator=: (implicit out this) = { } + operator=: (implicit out this, l: lineno_t) = lineno = l; + operator=: (implicit out this, l: lineno_t, c: colno_t) = + { + lineno = l; + colno = c; + } + + to_string: (this) -> std::string = "((lineno)$,(colno)$)"; +} + + +passing_style: @dll_visible @enum type = +{ + in; + copy; + inout; + out; + move; + forward; + invalid; +} + +// Not `passing_style::from_string` to workaround #555. +to_passing_style: (s: std::string_view) -> passing_style = +{ + return inspect s -> passing_style { + is (passing_style::in.to_string()) = passing_style::in; + is (passing_style::copy.to_string()) = passing_style::copy; + is (passing_style::inout.to_string()) = passing_style::inout; + is (passing_style::out.to_string()) = passing_style::out; + is (passing_style::move.to_string()) = passing_style::move; + is (passing_style::forward.to_string()) = passing_style::forward; + is _ = passing_style::invalid; + }; +} + + //----------------------------------------------------------------------- // // Compiler services @@ -29,62 +74,55 @@ meta: namespace = { //----------------------------------------------------------------------- // -compiler_services: @polymorphic_base @copyable type = +compiler_services: @dll_visible @polymorphic_base @copyable type = { // Common data members // - errors : *std::vector; - errors_original_size : int; - generated_tokens : *std::deque; - parser : cpp2::parser; - metafunction_name : std::string = (); - metafunction_args : std::vector = (); - metafunctions_used : bool = false; + data_: std::any; + private data: (this) -> forward _ = std::any_cast>(data_); + private data: (inout this) -> forward _ = std::any_cast>(data_); // Constructor // operator=: ( out this, - errors_ : *std::vector, - generated_tokens_: *std::deque + data_v: std::any ) = { - errors = errors_; - errors_original_size = cpp2::unsafe_narrow(std::ssize(errors*)); - generated_tokens = generated_tokens_; - parser = errors*; + data_ = data_v; + assert( data_.type() == Typeid(), "parameter 'data_v' must store a 'compiler_services_data'" ); } // Common API // set_metafunction_name: (inout this, name: std::string_view, args: std::vector) = { - metafunction_name = name; - metafunction_args = args; - metafunctions_used = args.empty(); + data().metafunction_name = name; + data().metafunction_args = args; + data().metafunctions_used = args.empty(); } - get_metafunction_name: (this) -> std::string_view = metafunction_name; + get_metafunction_name: (this) -> std::string_view = data().metafunction_name; get_argument: (inout this, index: int) -> std::string = { - metafunctions_used = true; - if (0 <= index < metafunction_args.ssize()) { - return metafunction_args[index]; + data().metafunctions_used = true; + if (0 <= index < data().metafunction_args.ssize()) { + return data().metafunction_args[index]; } return ""; } get_arguments: (inout this) -> std::vector = { - metafunctions_used = true; - return metafunction_args; + data().metafunctions_used = true; + return data().metafunction_args; } - arguments_were_used: (this) -> bool = metafunctions_used; + arguments_were_used: (this) -> bool = data().metafunctions_used; protected parse_statement: ( inout this, copy source: std::string_view ) - -> (ret: std::unique_ptr) + -> _ = { original_source := source; @@ -116,7 +154,7 @@ compiler_services: @polymorphic_base @copyable type = // Now lex this source fragment to generate // a single grammar_map entry, whose .second // is the vector of tokens - _ = generated_lexers.emplace_back( errors* ); + _ = generated_lexers.emplace_back( data().errors* ); tokens := generated_lexers.back()&; tokens*.lex( lines*, true ); @@ -124,13 +162,14 @@ compiler_services: @polymorphic_base @copyable type = // Now parse this single declaration from // the lexed tokens - ret = parser.parse_one_declaration( - tokens*.get_map().begin()*.second, - generated_tokens* - ); + ret := data().parser.parse_one_declaration( + tokens*.get_map().begin()*.second, + data().generated_tokens* + ); if !ret { error( "parse failed - the source string is not a valid statement:\n(original_source)$"); } + return ret; } position: (virtual this) @@ -156,10 +195,10 @@ compiler_services: @polymorphic_base @copyable type = error: (this, msg: std::string_view) = { message := msg as std::string; - if !metafunction_name.empty() { - message = "while applying @(metafunction_name)$ - (message)$"; + if !data().metafunction_name.empty() { + message = "while applying @(data().metafunction_name)$ - (message)$"; } - _ = errors*.emplace_back( position(), message); + _ = data().errors*.emplace_back( position(), message); } // Enable custom contracts on this object, integrated with compiler output @@ -167,7 +206,7 @@ compiler_services: @polymorphic_base @copyable type = // report_violation: (this, msg) = { error(msg); - throw( std::runtime_error(" ==> programming bug found in metafunction @(metafunction_name)$ - contract violation - see previous errors") ); + throw( std::runtime_error(" ==> programming bug found in metafunction @(data().metafunction_name)$ - contract violation - see previous errors") ); } has_handler:(this) true; @@ -184,7 +223,7 @@ compiler_services: @polymorphic_base @copyable type = // All type_ids are wrappers around a pointer to node // -type_id: @polymorphic_base @copyable type = +type_id: @dll_visible @polymorphic_base @copyable type = { this: compiler_services = (); @@ -220,21 +259,37 @@ type_id: @polymorphic_base @copyable type = // All declarations are wrappers around a pointer to node // -declaration_base: @polymorphic_base @copyable type = +declaration_base: @dll_visible @polymorphic_base @copyable type = { this: compiler_services = (); - protected n: *declaration_node; + node_pointer: @copyable type = + { + n: *void; + + operator=: ( + implicit out this, + n_: T + ) + = { + n = n_; + assert( n_, "a meta::declaration must point to a valid declaration_node, not null" ); + static_assert( std::is_same_v ); + } + + operator*: (this) -> forward _ = unsafe_narrow<*declaration_node>(n)*; + } + + protected n: node_pointer; protected operator=: ( out this, - n_: *declaration_node, + n_: node_pointer, s : compiler_services ) = { compiler_services = s; n = n_; - assert( n, "a meta::declaration must point to a valid declaration_node, not null" ); } position: (override this) -> source_position = n*.position(); @@ -246,13 +301,13 @@ declaration_base: @polymorphic_base @copyable type = //----------------------------------------------------------------------- // All declarations // -declaration: @polymorphic_base @copyable type = +declaration: @dll_visible @polymorphic_base @copyable type = { this: declaration_base = (); operator=: ( out this, - n_: *declaration_node, + n_: declaration_base::node_pointer, s : compiler_services ) = { @@ -272,6 +327,9 @@ declaration: @polymorphic_base @copyable type = make_protected : (inout this) -> bool = n*.make_protected(); make_private : (inout this) -> bool = n*.make_private(); + is_dll_visible : (this) -> bool = n*.is_dll_visible(); + make_dll_visible : (inout this) -> bool = n*.make_dll_visible(); + has_name : (this) -> bool = n*.has_name(); has_name : (this, s: std::string_view) -> bool = n*.has_name(s); @@ -328,13 +386,13 @@ declaration: @polymorphic_base @copyable type = //----------------------------------------------------------------------- // Function declarations // -function_declaration: @copyable type = +function_declaration: @dll_visible @copyable type = { this: declaration = (); operator=: ( out this, - n_: *declaration_node, + n_: declaration_base::node_pointer, s : compiler_services ) = { @@ -396,7 +454,7 @@ function_declaration: @copyable type = pre (!has_initializer(), "cannot add an initializer to a function that already has one") pre (parent_is_type(), "cannot add an initializer to a function that isn't in a type scope") = { - //require( !has_initializer(), + //require( !has_initializer(), // "cannot add an initializer to a function that already has one"); //require( parent_is_type(), // "cannot add an initializer to a function that isn't in a type scope"); @@ -415,13 +473,13 @@ function_declaration: @copyable type = //----------------------------------------------------------------------- // Object declarations // -object_declaration: @copyable type = +object_declaration: @dll_visible @copyable type = { this: declaration = (); operator=: ( out this, - n_: *declaration_node, + n_: declaration_base::node_pointer, s : compiler_services ) = { @@ -451,13 +509,13 @@ object_declaration: @copyable type = //----------------------------------------------------------------------- // Type declarations // -type_declaration: @copyable type = +type_declaration: @dll_visible @copyable type = { this: declaration = (); operator=: ( out this, - n_: *declaration_node, + n_: declaration_base::node_pointer, s : compiler_services ) = { @@ -485,7 +543,7 @@ type_declaration: @copyable type = -> std::vector = { ret: std::vector = (); - for n*.get_type_scope_declarations(declaration_node::functions) + for n*.get_type_scope_declarations(declaration_node::functions) do (d) { _ = ret.emplace_back( d, this ); } @@ -586,13 +644,13 @@ type_declaration: @copyable type = //----------------------------------------------------------------------- // Alias declarations // -alias_declaration: @copyable type = +alias_declaration: @dll_visible @copyable type = { this: declaration = (); operator=: ( out this, - n_: *declaration_node, + n_: declaration_base::node_pointer, s : compiler_services ) = { @@ -632,7 +690,7 @@ add_virtual_destructor: (inout t: meta::type_declaration) = // // an abstract base class having only pure virtual functions // -interface: (inout t: meta::type_declaration) = +interface: @dll_visible (inout t: meta::type_declaration) = { has_dtor := false; @@ -678,7 +736,7 @@ interface: (inout t: meta::type_declaration) = // // Unlike an interface, it can have nonpublic and nonvirtual functions. // -polymorphic_base: (inout t: meta::type_declaration) = +polymorphic_base: @dll_visible (inout t: meta::type_declaration) = { has_dtor := false; @@ -752,7 +810,7 @@ ordered_impl: ( // // Note: the ordering that should be encouraged as default gets the nice name // -ordered: (inout t: meta::type_declaration) = +ordered: @dll_visible (inout t: meta::type_declaration) = { ordered_impl( t, "strong_ordering" ); } @@ -760,7 +818,7 @@ ordered: (inout t: meta::type_declaration) = //----------------------------------------------------------------------- // weakly_ordered - a weakly ordered type // -weakly_ordered: (inout t: meta::type_declaration) = +weakly_ordered: @dll_visible (inout t: meta::type_declaration) = { ordered_impl( t, "weak_ordering" ); } @@ -768,7 +826,7 @@ weakly_ordered: (inout t: meta::type_declaration) = //----------------------------------------------------------------------- // partially_ordered - a partially ordered type // -partially_ordered: (inout t: meta::type_declaration) = +partially_ordered: @dll_visible (inout t: meta::type_declaration) = { ordered_impl( t, "partial_ordering" ); } @@ -790,7 +848,7 @@ partially_ordered: (inout t: meta::type_declaration) = // // A type with (copy and move) x (construction and assignment) // -copyable: (inout t: meta::type_declaration) = +copyable: @dll_visible (inout t: meta::type_declaration) = { // If the user explicitly wrote any of the copy/move functions, // they must also have written the most general one - we can't @@ -818,7 +876,7 @@ copyable: (inout t: meta::type_declaration) = // A regular type: copyable, plus has public default construction // and no protected or virtual functions // -basic_value: (inout t: meta::type_declaration) = +basic_value: @dll_visible (inout t: meta::type_declaration) = { t.copyable(); @@ -846,19 +904,19 @@ basic_value: (inout t: meta::type_declaration) = // // Note: the ordering that should be encouraged as default gets the nice name // -value: (inout t: meta::type_declaration) = +value: @dll_visible (inout t: meta::type_declaration) = { t.ordered(); t.basic_value(); } -weakly_ordered_value: (inout t: meta::type_declaration) = +weakly_ordered_value: @dll_visible (inout t: meta::type_declaration) = { t.weakly_ordered(); t.basic_value(); } -partially_ordered_value: (inout t: meta::type_declaration) = +partially_ordered_value: @dll_visible (inout t: meta::type_declaration) = { t.partially_ordered(); t.basic_value(); @@ -890,7 +948,7 @@ partially_ordered_value: (inout t: meta::type_declaration) = // no virtual functions, and no user-defined constructors // (i.e., no invariants) or assignment or destructors. // -struct: (inout t: meta::type_declaration) = +struct: @dll_visible (inout t: meta::type_declaration) = { for t.get_members() do (inout m) { @@ -1123,7 +1181,7 @@ basic_enum: ( // // -- P0707R4, section 3 // -enum: (inout t: meta::type_declaration) = +enum: @dll_visible (inout t: meta::type_declaration) = { // Let basic_enum do its thing, with an incrementing value generator t.basic_enum( @@ -1150,7 +1208,7 @@ enum: (inout t: meta::type_declaration) = // // -- P0707R4, section 3 // -flag_enum: (inout t: meta::type_declaration) = +flag_enum: @dll_visible (inout t: meta::type_declaration) = { // Let basic_enum do its thing, with a power-of-two value generator t.basic_enum( @@ -1196,7 +1254,7 @@ flag_enum: (inout t: meta::type_declaration) = // a type that contains exactly one of a fixed set of values at a time // -union: (inout t : meta::type_declaration) +union: @dll_visible (inout t : meta::type_declaration) = { alternatives : std::vector = (); @@ -1258,8 +1316,8 @@ union: (inout t : meta::type_declaration) storage += "), cpp2::max( "; (copy comma: std::string = "") - for alternatives - next comma = ", " + for alternatives + next comma = ", " do (e) { storage += comma + "alignof((e.type)$)"; } @@ -1332,7 +1390,7 @@ union: (inout t : meta::type_declaration) // // print - output a pretty-printed visualization of t // -print: (t: meta::type_declaration) = +print: @dll_visible (t: meta::type_declaration) = { std::cout << t.print() << "\n"; } @@ -1340,105 +1398,17 @@ print: (t: meta::type_declaration) = //----------------------------------------------------------------------- // -// apply_metafunctions +// dll_visible - makes t visible in a DLL // -apply_metafunctions: ( - inout n : declaration_node, - inout rtype : type_declaration, - error - ) - -> bool -= { - assert( n.is_type() ); - - // Check for _names reserved for the metafunction implementation - for rtype.get_members() - do (m) - { - m.require( !m.name().starts_with("_") || m.name().ssize() > 1, - "a type that applies a metafunction cannot have a body that declares a name that starts with '_' - those names are reserved for the metafunction implementation"); - } - - // For each metafunction, apply it - for n.metafunctions - do (meta) - { - // Convert the name and any template arguments to strings - // and record that in rtype - name := meta*.to_string(); - name = name.substr(0, name.find('<')); - - args: std::vector = (); - for meta*.template_arguments() - do (arg) - args.push_back( arg.to_string() ); - - rtype.set_metafunction_name( name, args ); - - // Dispatch - // - if name == "interface" { - interface( rtype ); - } - else if name == "polymorphic_base" { - polymorphic_base( rtype ); - } - else if name == "ordered" { - ordered( rtype ); - } - else if name == "weakly_ordered" { - weakly_ordered( rtype ); - } - else if name == "partially_ordered" { - partially_ordered( rtype ); - } - else if name == "copyable" { - copyable( rtype ); - } - else if name == "basic_value" { - basic_value( rtype ); - } - else if name == "value" { - value( rtype ); - } - else if name == "weakly_ordered_value" { - weakly_ordered_value( rtype ); - } - else if name == "partially_ordered_value" { - partially_ordered_value( rtype ); - } - else if name == "struct" { - cpp2_struct( rtype ); - } - else if name == "enum" { - cpp2_enum( rtype ); - } - else if name == "flag_enum" { - flag_enum( rtype ); - } - else if name == "union" { - cpp2_union( rtype ); - } - else if name == "print" { - print( rtype ); - } - else { - error( "unrecognized metafunction name: " + name ); - error( "(temporary alpha limitation) currently the supported names are: interface, polymorphic_base, ordered, weakly_ordered, partially_ordered, copyable, basic_value, value, weakly_ordered_value, partially_ordered_value, struct, enum, flag_enum, union, print" ); - return false; - } - - if ( - !args.empty() - && !rtype.arguments_were_used() - ) - { - error( name + " did not use its template arguments - did you mean to write '" + name + " <" + args[0] + "> type' (with the spaces)?"); - return false; - } - } - - return true; +dll_visible: @dll_visible (inout t: meta::type_declaration) = +{ + t.require( t.make_dll_visible(), + "dll_visible can only be applied to a namespace-scope name"); +} +dll_visible: @dll_visible (inout t: meta::function_declaration) = +{ + t.require( t.make_dll_visible(), + "dll_visible can only be applied to a namespace-scope name"); } diff --git a/source/reflect_impl.h b/source/reflect_impl.h new file mode 100644 index 000000000..4e5295117 --- /dev/null +++ b/source/reflect_impl.h @@ -0,0 +1,893 @@ + +#ifndef REFLECT_IMPL_H_CPP2 +#define REFLECT_IMPL_H_CPP2 + + +//=== Cpp2 type declarations ==================================================== + + +#include "cpp2util.h" + +#line 1 "reflect_impl.h2" + +#line 31 "reflect_impl.h2" +namespace cpp2 { + +namespace meta { + +#line 54 "reflect_impl.h2" +class dll_symbol; + + +#line 115 "reflect_impl.h2" +class diagnostic; + + +#line 119 "reflect_impl.h2" +template class expected; + + +#line 136 "reflect_impl.h2" +} + +} + +#line 397 "reflect_impl.h2" +namespace cpp2 { + +namespace meta { + +#line 409 "reflect_impl.h2" +class compiler_services_data; + +#line 622 "reflect_impl.h2" +} + +} + + +//=== Cpp2 type definitions and function declarations =========================== + +#line 1 "reflect_impl.h2" + +// Copyright (c) Herb Sutter +// SPDX-License-Identifier: CC-BY-NC-ND-4.0 + +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + + +//=========================================================================== +// Reflection and meta +//=========================================================================== + +#include "parse.h" +#include +#include +#include +#ifdef _WIN32 +#define WIN32_LEAN_AND_MEAN +#define NOMINMAX +#include +#else +#include +#endif // _WIN32 + +#line 31 "reflect_impl.h2" +namespace cpp2 { + +namespace meta { + +#line 36 "reflect_impl.h2" +[[nodiscard]] auto to_type_metafunction_cast( + cpp2::in name, + cpp2::in is_const_metafunction + ) -> std::string; + +#line 50 "reflect_impl.h2" +//----------------------------------------------------------------------- +// +// dll_symbol +// +class dll_symbol { + private: std::string value {}; + + public: static const std::string_view constant_prefix; + public: static const std::string_view reachable_mangle; + public: static const std::string_view const_metafunction_mangle; + + public: dll_symbol(); + public: explicit dll_symbol(cpp2::in n, cpp2::in is_reachable_); + +#line 76 "reflect_impl.h2" + public: explicit dll_symbol(std::string_view s); +#line 76 "reflect_impl.h2" + public: auto operator=(std::string_view s) -> dll_symbol& ; + +#line 84 "reflect_impl.h2" + public: [[nodiscard]] auto c_str() const& -> char const*; + public: [[nodiscard]] auto view() const& -> std::string_view; + public: [[nodiscard]] auto without_prefix() const& -> std::string_view; + +#line 99 "reflect_impl.h2" + public: [[nodiscard]] auto is_reachable() const& -> bool; + public: [[nodiscard]] auto is_const_metafunction() const& -> bool; + public: [[nodiscard]] auto operator<=>(dll_symbol const& that) const& -> std::strong_ordering = default; +public: dll_symbol(dll_symbol const& that); + +public: auto operator=(dll_symbol const& that) -> dll_symbol& ; +public: dll_symbol(dll_symbol&& that) noexcept; +public: auto operator=(dll_symbol&& that) noexcept -> dll_symbol& ; + +#line 104 "reflect_impl.h2" +}; + +[[nodiscard]] auto symbols_accessor() -> dll_symbol; + +#line 111 "reflect_impl.h2" +//----------------------------------------------------------------------- +// +// expected with diagnostic to return to apply_metafunctions +// +class diagnostic { + public: std::string value; +}; + +template class expected { + +#line 123 "reflect_impl.h2" + public: expected(T const& v); +#line 123 "reflect_impl.h2" + public: auto operator=(T const& v) -> expected& ; + public: expected(cpp2::in u); +#line 124 "reflect_impl.h2" + public: auto operator=(cpp2::in u) -> expected& ; + + public: template [[nodiscard]] auto and_then(F const& f) && -> std::remove_cvref_t>; + private: cpp2::aligned_storage _storage {}; private: cpp2::i8 _discriminator {-1}; public: [[nodiscard]] auto is_value() const& -> bool; +public: [[nodiscard]] auto value() const& -> T const&; +public: [[nodiscard]] auto value() & -> T&; +public: auto set_value(cpp2::in _value) & -> void; +public: auto set_value(auto&& ..._args) & -> void; +public: [[nodiscard]] auto is_error() const& -> bool; +public: [[nodiscard]] auto error() const& -> diagnostic const&; +public: [[nodiscard]] auto error() & -> diagnostic&; +public: auto set_error(cpp2::in _value) & -> void; +public: auto set_error(auto&& ..._args) & -> void; +private: auto _destroy() & -> void; +public: ~expected() noexcept; +public: explicit expected(); +public: expected(expected const& that); + +public: expected(expected&& that) noexcept; +public: auto operator=(expected const& that) -> expected& ; +public: auto operator=(expected&& that) noexcept -> expected& ; + +#line 134 "reflect_impl.h2" +}; + +} + +} + + +namespace cpp2::meta { + + +//----------------------------------------------------------------------- +// +// (de)mangling +// +auto mangle(std::string res) + -> std::string +{ + // Mangle (id length, id) pairs according to + // https://en.wikipedia.org/wiki/Name_mangling#Complex_example + // "... by the GNU GCC 3.x compilers, according to the IA-64 (Itanium) ABI" + auto xpos = res.size(); + auto prev_id_end = xpos; + while ((xpos = res.find_last_of(':', xpos)) != res.npos) + { + res.replace(xpos - 1, 2, std::to_string(prev_id_end - xpos - 1)); + prev_id_end = xpos - 1; + } + + return res; +} + +auto mangle(declaration_node const& n) + -> std::string +{ + assert(n.identifier); + assert(n.identifier->template_arguments().empty()); + assert(n.parent_is_namespace()); + + return mangle(n.fully_qualified_name()); +} + +auto demangle(std::string_view s) + -> std::string +{ + std::string res{}; + + // Demangle (id length, id) pairs according to + // https://en.wikipedia.org/wiki/Name_mangling#Complex_example + // "... by the GNU GCC 3.x compilers, according to the IA-64 (Itanium) ABI" + while (!s.empty()) + { + auto length = s.substr(0, s.find_first_not_of("0123456789")); + s.remove_prefix(length.size()); + auto id = s.substr(0, unsafe_narrow(std::stoi(std::string{length}))); + s.remove_prefix(id.size()); + + assert(!length.empty()); + assert(!id.empty()); + + res += id; + if (!s.empty()) { + res += "::"; + } + } + + return res; +} + + +//----------------------------------------------------------------------- +// +// dll Load DLL and its symbols with the OS API +// +class dll +{ +public: + dll(std::string const& path) + : handle_{ +#ifdef _WIN32 + static_cast(LoadLibraryA(path.c_str())) +#else + static_cast(dlopen(path.c_str(), RTLD_NOW|RTLD_LOCAL)) +#endif // _WIN32 + , +[](void* handle) + { +#ifdef _WIN32 + FreeLibrary(static_cast(handle)); +#else + dlclose(handle); +#endif // _WIN32 + } + } + { + if(!handle_) { + Default.report_violation(("failed to load DLL '" + path + "': " + get_last_error()).c_str()); + } + } + + template + auto get_alias(std::string const& name) noexcept -> T& + { +#ifdef _WIN32 + auto symbol = reinterpret_cast(GetProcAddress(static_cast(handle_.get()), name.c_str())); +#else + auto symbol = dlsym(handle_.get(), name.c_str()); + if(!symbol) + { + // Some platforms export with additional underscore, so try that + auto const us_name = "_" + name; + symbol = dlsym(handle_.get(), us_name.c_str()); + } +#endif // _WIN32 + if (!symbol) { + Default.report_violation(("failed to load DLL symbol '" + name+ "': " + get_last_error()).c_str()); + } + return **reinterpret_cast(symbol); + } +private: + std::shared_ptr handle_; + + static auto get_last_error() noexcept -> std::string + { +#ifdef _WIN32 + DWORD errorMessageID = GetLastError(); + if(errorMessageID == 0) { + return {}; // No error message has been recorded + } + LPSTR messageBuffer = nullptr; + auto size = FormatMessageA( + FORMAT_MESSAGE_ALLOCATE_BUFFER|FORMAT_MESSAGE_FROM_SYSTEM|FORMAT_MESSAGE_IGNORE_INSERTS, + nullptr, + errorMessageID, + MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US), + (LPSTR)&messageBuffer, + 0, + nullptr + ); + std::string message(messageBuffer, unsafe_narrow(size)); + LocalFree(messageBuffer); + return message; +#else + return std::string{dlerror()}; +#endif // _WIN32 + } + +}; + + +//----------------------------------------------------------------------- +// +// get_reachable_metafunction_symbols +// +struct library +{ + std::string_view name; + std::vector<::cpp2::meta::record> symbols; +}; + +namespace this_execution { + +// Load Cpp2 libraries with metafunctions by opening DLL with the OS API +// +// The environment variable 'CPPFRONT_METAFUNCTION_LIBRARIES' +// is read and interpreted as ':'-separated Cpp2 metafunction library paths +std::span get_reachable_metafunction_symbols() +{ + static std::vector res = []{ + std::vector res; + + // FIXME: On Windows, using this approach with the system apis not set to utf8, will + // break if a metafunction library contains unicode codepoints in its name, a proper + // way to handle this would be to use _wgetenv and use wchar_t strings for the dll opening + // function + auto cpp1_libraries_cstr = std::getenv("CPPFRONT_METAFUNCTION_LIBRARIES"); + if ( + !cpp1_libraries_cstr + || cpp1_libraries_cstr[0] == '\0' + ) + { + return res; + } + + auto cpp1_libraries = std::string_view{cpp1_libraries_cstr}; + while (!cpp1_libraries.empty()) + { + auto colon = cpp1_libraries.find(':'); + auto lib_path = cpp1_libraries.substr(0, colon); + cpp1_libraries.remove_prefix(lib_path.size() + unsigned(colon != lib_path.npos)); + + // TODO: Ask Johel why are DLLs now loaded into perpetuity? + auto lib = dll{std::string{lib_path}}; + + auto get_symbols = lib.get_alias("cpp2_meta_registry"); + + size_t registry_size; + auto* registry_data = static_cast<::cpp2::meta::record*>(get_symbols(®istry_size)); + + res.push_back({lib_path, {}}); + if (registry_size == 0) { + Default.report_violation( + ("symbols accesor returns no symbols (in '" + std::string{lib_path} + "')").c_str() + ); + } + + for (size_t i = 0; i < registry_size; i++) { + auto symbol = res.back().symbols.emplace_back(registry_data[i]); + } + } + + return res; + }(); + + return res; +} + +} + + +//----------------------------------------------------------------------- +// +// load_metafunction +// +struct lookup_res { + std::string_view library; + ::cpp2::meta::record const* symbol; +}; + +using load_metafunction_ret = std::function; + +// Load Cpp2 metafunction by opening DLL with the OS API +auto load_metafunction( + std::string const& name, + std::function(std::string const&)> lookup + ) + -> expected +{ + return lookup(name).and_then( + [](lookup_res res) + -> expected + { + auto [lib_path, record] = res; + + // TODO: Ask Johel why the double load if we never unload? + auto lib = dll{std::string(lib_path)}; + assert(record->function.index() == 1); + return load_metafunction_ret{ + [ + fun = std::get<1>(record->function), + lib = std::move(lib) + ] + (type_declaration& t) + -> void + { + fun(t); + } + }; + } + ); +} + +} + +#line 397 "reflect_impl.h2" +namespace cpp2 { + +namespace meta { + +#line 402 "reflect_impl.h2" +//----------------------------------------------------------------------- +// +// Compiler services data +// +//----------------------------------------------------------------------- +// + +class compiler_services_data + { + // Common data members + // + public: std::vector* errors; + public: int errors_original_size; + public: std::deque* generated_tokens; + public: cpp2::parser parser; + public: std::string metafunction_name {}; + public: std::vector metafunction_args {}; + public: bool metafunctions_used {false}; + + // Make function + // + public: [[nodiscard]] static auto make( + std::vector* errors_, + std::deque* generated_tokens_, + cpp2::in translation_unit_has_interface + ) -> compiler_services_data; + +#line 435 "reflect_impl.h2" +}; + +#line 438 "reflect_impl.h2" +//----------------------------------------------------------------------- +// +// apply_metafunctions +// +[[nodiscard]] auto apply_metafunctions( + declaration_node& n, + type_declaration& rtype, + auto const& error, + auto const& lookup + ) -> bool; + +#line 556 "reflect_impl.h2" +[[nodiscard]] auto apply_metafunctions( + declaration_node& n, + function_declaration& rfunction, + auto const& error + ) -> bool; + +#line 622 "reflect_impl.h2" +} + +} + + +#include "cpp2reflect.hpp" + + +//=== Cpp2 function definitions ================================================= + +#line 1 "reflect_impl.h2" + +#line 31 "reflect_impl.h2" +namespace cpp2 { + +namespace meta { + +#line 36 "reflect_impl.h2" +[[nodiscard]] auto to_type_metafunction_cast( + cpp2::in name, + cpp2::in is_const_metafunction + ) -> std::string + +{ + auto const_ {""}; + if (is_const_metafunction) { + const_ = " const"; + } + return "static_cast(" + cpp2::to_string(name) + ")"; +} + +#line 57 "reflect_impl.h2" + inline CPP2_CONSTEXPR std::string_view dll_symbol::constant_prefix = "cpp2_metafunction_"; + inline CPP2_CONSTEXPR std::string_view dll_symbol::reachable_mangle = "r"; + inline CPP2_CONSTEXPR std::string_view dll_symbol::const_metafunction_mangle = "c"; + + dll_symbol::dll_symbol(){} + dll_symbol::dll_symbol(cpp2::in n, cpp2::in is_reachable_) + : value{ constant_prefix } + +#line 66 "reflect_impl.h2" + { + cpp2::finally_presuccess cpp2_finally_presuccess; + if (cpp2::Default.has_handler() && !(CPP2_UFCS(is_metafunction)(n)) ) { cpp2::Default.report_violation(""); } + cpp2_finally_presuccess.add([&]{if (cpp2::Default.has_handler() && !(is_reachable() == is_reachable_) ) { cpp2::Default.report_violation(""); }} ); + cpp2_finally_presuccess.add([&]{if (cpp2::Default.has_handler() && !(is_const_metafunction() == CPP2_UFCS(is_const_metafunction)(n)) ) { cpp2::Default.report_violation(""); }} ); + +#line 68 "reflect_impl.h2" + if (is_reachable_) { + value += reachable_mangle; + } + if (CPP2_UFCS(is_const_metafunction)(n)) { + value += const_metafunction_mangle; + } + value += mangle(n); + } + dll_symbol::dll_symbol(std::string_view s) + { + if (!(CPP2_UFCS(starts_with)(s, constant_prefix))) { + value += constant_prefix; + } + value += std::move(s); + } +#line 76 "reflect_impl.h2" + auto dll_symbol::operator=(std::string_view s) -> dll_symbol& + { + value = {}; +#line 78 "reflect_impl.h2" + if (!(CPP2_UFCS(starts_with)(s, constant_prefix))) { + value += constant_prefix; + } + value += std::move(s); + return *this; +#line 82 "reflect_impl.h2" + } + + [[nodiscard]] auto dll_symbol::c_str() const& -> char const* { return CPP2_UFCS(c_str)(value); } + [[nodiscard]] auto dll_symbol::view() const& -> std::string_view { return value; } + [[nodiscard]] auto dll_symbol::without_prefix() const& -> std::string_view + + { + auto res {view()}; + CPP2_UFCS(remove_prefix)(res, CPP2_UFCS(size)(constant_prefix)); + for ( + auto const& m : { reachable_mangle, const_metafunction_mangle } ) + if (CPP2_UFCS(starts_with)(res, m)) { + CPP2_UFCS(remove_prefix)(res, CPP2_UFCS(size)(m)); + } + return res; + } + + [[nodiscard]] auto dll_symbol::is_reachable() const& -> bool { return CPP2_UFCS(starts_with)(CPP2_UFCS(substr)(view(), CPP2_UFCS(size)(constant_prefix)), reachable_mangle); } + [[nodiscard]] auto dll_symbol::is_const_metafunction() const& -> bool { + return CPP2_UFCS(starts_with)(CPP2_UFCS(substr)(CPP2_UFCS(substr)(view(), CPP2_UFCS(size)(constant_prefix)) + , unsigned(is_reachable()) * CPP2_UFCS(size)(reachable_mangle)) + , const_metafunction_mangle); } + + + dll_symbol::dll_symbol(dll_symbol const& that) + : value{ that.value }{} + +auto dll_symbol::operator=(dll_symbol const& that) -> dll_symbol& { + value = that.value; + return *this;} +dll_symbol::dll_symbol(dll_symbol&& that) noexcept + : value{ std::move(that).value }{} +auto dll_symbol::operator=(dll_symbol&& that) noexcept -> dll_symbol& { + value = std::move(that).value; + return *this;} +#line 106 "reflect_impl.h2" +[[nodiscard]] auto symbols_accessor() -> dll_symbol { + + return dll_symbol("get_symbol_names"); } + +#line 123 "reflect_impl.h2" + template expected::expected(T const& v) { set_value(v); } +#line 123 "reflect_impl.h2" + template auto expected::operator=(T const& v) -> expected& { + _storage = {}; + _discriminator = -1; set_value(v); + return *this; } +#line 124 "reflect_impl.h2" + template expected::expected(cpp2::in u) { set_error(u); } +#line 124 "reflect_impl.h2" + template auto expected::operator=(cpp2::in u) -> expected& { + _storage = {}; + _discriminator = -1; set_error(u); + return *this; } + +#line 126 "reflect_impl.h2" + template template [[nodiscard]] auto expected::and_then(F const& f) && -> std::remove_cvref_t>{ + if (is_value()) { + return f(value()); + } + else { + return { error() }; + } + } + + + + template [[nodiscard]] auto expected::is_value() const& -> bool { return _discriminator == 0; } +template [[nodiscard]] auto expected::value() const& -> T const& { + if (cpp2::Default.has_handler() && !(is_value()) ) { cpp2::Default.report_violation(""); }return *cpp2::assert_not_null(reinterpret_cast(&_storage)); } +template [[nodiscard]] auto expected::value() & -> T& { + if (cpp2::Default.has_handler() && !(is_value()) ) { cpp2::Default.report_violation(""); }return *cpp2::assert_not_null(reinterpret_cast(&_storage)); } +template auto expected::set_value(cpp2::in _value) & -> void{if (!(is_value())) {_destroy();std::construct_at(reinterpret_cast(&_storage), _value);}else {*cpp2::assert_not_null(reinterpret_cast(&_storage)) = _value;}_discriminator = 0;} +template auto expected::set_value(auto&& ..._args) & -> void{if (!(is_value())) {_destroy();std::construct_at(reinterpret_cast(&_storage), _args...);}else {*cpp2::assert_not_null(reinterpret_cast(&_storage)) = T{_args...};}_discriminator = 0;} +template [[nodiscard]] auto expected::is_error() const& -> bool { return _discriminator == 1; } +template [[nodiscard]] auto expected::error() const& -> diagnostic const& { + if (cpp2::Default.has_handler() && !(is_error()) ) { cpp2::Default.report_violation(""); }return *cpp2::assert_not_null(reinterpret_cast(&_storage)); } +template [[nodiscard]] auto expected::error() & -> diagnostic& { + if (cpp2::Default.has_handler() && !(is_error()) ) { cpp2::Default.report_violation(""); }return *cpp2::assert_not_null(reinterpret_cast(&_storage)); } +template auto expected::set_error(cpp2::in _value) & -> void{if (!(is_error())) {_destroy();std::construct_at(reinterpret_cast(&_storage), _value);}else {*cpp2::assert_not_null(reinterpret_cast(&_storage)) = _value;}_discriminator = 1;} +template auto expected::set_error(auto&& ..._args) & -> void{if (!(is_error())) {_destroy();std::construct_at(reinterpret_cast(&_storage), _args...);}else {*cpp2::assert_not_null(reinterpret_cast(&_storage)) = diagnostic{_args...};}_discriminator = 1;} +template auto expected::_destroy() & -> void{ + if (_discriminator == 0) {std::destroy_at(reinterpret_cast(&_storage));} + if (_discriminator == 1) {std::destroy_at(reinterpret_cast(&_storage));} + _discriminator = -1; + } + + template expected::~expected() noexcept{_destroy();} +template expected::expected(){} +template expected::expected(expected const& that) + : _storage{ } + , _discriminator{ -1 }{ + if (CPP2_UFCS(is_value)(that)) {set_value(CPP2_UFCS(value)(that));} + if (CPP2_UFCS(is_error)(that)) {set_error(CPP2_UFCS(error)(that));} + } + + + template expected::expected(expected&& that) noexcept + : _storage{ } + , _discriminator{ -1 }{ + if (CPP2_UFCS(is_value)(std::move(that))) {set_value(CPP2_UFCS(value)(std::move(that)));} + if (CPP2_UFCS(is_error)(std::move(that))) {set_error(CPP2_UFCS(error)(std::move(that)));} + } + + template auto expected::operator=(expected const& that) -> expected& { + if (CPP2_UFCS(is_value)(that)) {set_value(CPP2_UFCS(value)(that));} + if (CPP2_UFCS(is_error)(that)) {set_error(CPP2_UFCS(error)(that));} + return *this; + } + + template auto expected::operator=(expected&& that) noexcept -> expected& { + if (CPP2_UFCS(is_value)(std::move(that))) {set_value(CPP2_UFCS(value)(std::move(that)));} + if (CPP2_UFCS(is_error)(std::move(that))) {set_error(CPP2_UFCS(error)(std::move(that)));} + return *this; + } +#line 136 "reflect_impl.h2" +} + +} + +#line 397 "reflect_impl.h2" +namespace cpp2 { + +namespace meta { + +#line 423 "reflect_impl.h2" + [[nodiscard]] auto compiler_services_data::make( + std::vector* errors_, + std::deque* generated_tokens_, + cpp2::in translation_unit_has_interface + ) -> compiler_services_data + + { + return { errors_, + cpp2::unsafe_narrow(std::ssize(*cpp2::assert_not_null(errors_))), + generated_tokens_, + cpp2::parser(*cpp2::assert_not_null(errors_), translation_unit_has_interface) }; + } + +#line 442 "reflect_impl.h2" +[[nodiscard]] auto apply_metafunctions( + declaration_node& n, + type_declaration& rtype, + auto const& error, + auto const& lookup + ) -> bool + +{ + if (cpp2::Default.has_handler() && !(CPP2_UFCS(is_type)(n)) ) { cpp2::Default.report_violation(""); } + + // Check for _names reserved for the metafunction implementation + for ( + auto const& m : CPP2_UFCS(get_members)(rtype) ) + { + CPP2_UFCS(require)(m, !(CPP2_UFCS(starts_with)(CPP2_UFCS(name)(m), "_")) || cpp2::cmp_greater(CPP2_UFCS(ssize)(CPP2_UFCS(name)(m)),1), + "a type that applies a metafunction cannot have a body that declares a name that starts with '_' - those names are reserved for the metafunction implementation"); + } + + // For each metafunction, apply it + for ( + auto const& meta : n.metafunctions ) + { + // Convert the name and any template arguments to strings + // and record that in rtype + auto name {CPP2_UFCS(to_string)((*cpp2::assert_not_null(meta)))}; + name = CPP2_UFCS(substr)(name, 0, CPP2_UFCS(find)(name, '<')); + + std::vector args {}; + for ( + auto const& arg : CPP2_UFCS(template_arguments)((*cpp2::assert_not_null(meta))) ) + CPP2_UFCS(push_back)(args, CPP2_UFCS(to_string)(arg)); + + CPP2_UFCS(set_metafunction_name)(rtype, name, args); + + // Dispatch + // + if (name == "interface") { + interface(rtype); + } + else {if (name == "polymorphic_base") { + polymorphic_base(rtype); + } + else {if (name == "ordered") { + ordered(rtype); + } + else {if (name == "weakly_ordered") { + weakly_ordered(rtype); + } + else {if (name == "partially_ordered") { + partially_ordered(rtype); + } + else {if (name == "copyable") { + copyable(rtype); + } + else {if (name == "basic_value") { + basic_value(rtype); + } + else {if (name == "value") { + value(rtype); + } + else {if (name == "weakly_ordered_value") { + weakly_ordered_value(rtype); + } + else {if (name == "partially_ordered_value") { + partially_ordered_value(rtype); + } + else {if (name == "struct") { + cpp2_struct(rtype); + } + else {if (name == "enum") { + cpp2_enum(rtype); + } + else {if (name == "flag_enum") { + flag_enum(rtype); + } + else {if (name == "union") { + cpp2_union(rtype); + } + else {if (name == "print") { + print(rtype); + } + else {if (name == "dll_visible") { + dll_visible(rtype); + } + else { +{ +auto const& load = load_metafunction(name, lookup); + +#line 528 "reflect_impl.h2" + if (CPP2_UFCS(is_value)(load)) { + CPP2_UFCS(value)(load)(rtype); + }else { + error("unrecognized metafunction name: " + name); + if (CPP2_UFCS(find)(name, "::") == name.npos) { + error("currently supported built-in names are: interface, polymorphic_base, ordered, weakly_ordered, partially_ordered, copyable, basic_value, value, weakly_ordered_value, partially_ordered_value, struct, enum, flag_enum, union, print, dll_visible"); + } + if (!(CPP2_UFCS(empty)(CPP2_UFCS(error)(load).value))) { + error(CPP2_UFCS(error)(load).value); + } + return false; + } +} +#line 540 "reflect_impl.h2" + }}}}}}}}}}}}}}}} + + if (( + !(CPP2_UFCS(empty)(args)) + && !(CPP2_UFCS(arguments_were_used)(rtype)))) + + { + error(name + " did not use its template arguments - did you mean to write '" + name + " <" + CPP2_ASSERT_IN_BOUNDS(args, 0) + "> type' (with the spaces)?"); + return false; + } + } + + return true; +} + +#line 556 "reflect_impl.h2" +[[nodiscard]] auto apply_metafunctions( + declaration_node& n, + function_declaration& rfunction, + auto const& error + ) -> bool + +{ + if (cpp2::Default.has_handler() && !(CPP2_UFCS(is_function)(n)) ) { cpp2::Default.report_violation(""); } + + // Check for _names reserved for the metafunction implementation +// for rfunction.get_members() +// do (m) +// { +// m.require( !m.name().starts_with("_") || m.name().ssize() > 1, +// "a function that applies a metafunction cannot have a body that declares a name that starts with '_' - those names are reserved for the metafunction implementation"); +// } + + // For each metafunction, apply it + for ( + auto const& meta : n.metafunctions ) + { + // Convert the name and any template arguments to strings + // and record that in rfunction + auto name {CPP2_UFCS(to_string)((*cpp2::assert_not_null(meta)))}; + name = CPP2_UFCS(substr)(name, 0, CPP2_UFCS(find)(name, '<')); + + std::vector args {}; + for ( + auto const& arg : CPP2_UFCS(template_arguments)((*cpp2::assert_not_null(meta))) ) + CPP2_UFCS(push_back)(args, CPP2_UFCS(to_string)(arg)); + + CPP2_UFCS(set_metafunction_name)(rfunction, name, args); + + // Dispatch + // + if (name == "dll_visible") { + dll_visible(rfunction); + } + else { +// (load := load_metafunction(name)) +// if load.metafunction { +// load.metafunction( rfunction ); +// } else { + error("unrecognized metafunction name: " + name); + error("currently supported built-in names are: dll_visible"); +// if !load.error.empty() { +// error( load.error ); +// } +// return false; +// } + } + + if (( + !(CPP2_UFCS(empty)(args)) + && !(CPP2_UFCS(arguments_were_used)(rfunction)))) + + { + error(name + " did not use its template arguments - did you mean to write '" + name + " <" + CPP2_ASSERT_IN_BOUNDS(args, 0) + "> (...) -> ...' (with the spaces)?"); + return false; + } + } + + return true; +} + +#line 622 "reflect_impl.h2" +} + +} + +#endif diff --git a/source/reflect_impl.h2 b/source/reflect_impl.h2 new file mode 100644 index 000000000..8c2fd32b5 --- /dev/null +++ b/source/reflect_impl.h2 @@ -0,0 +1,627 @@ + +// Copyright (c) Herb Sutter +// SPDX-License-Identifier: CC-BY-NC-ND-4.0 + +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + + +//=========================================================================== +// Reflection and meta +//=========================================================================== + +#include "parse.h" +#include +#include +#include +#ifdef _WIN32 +#define WIN32_LEAN_AND_MEAN +#define NOMINMAX +#include +#else +#include +#endif // _WIN32 + + +cpp2: namespace = { + +meta: namespace = { + + +to_type_metafunction_cast: ( + name: std::string_view, + is_const_metafunction: bool + ) + -> std::string += { + const_ := ""; + if is_const_metafunction { + const_ = " const"; + } + return "static_cast((name)$)"; +} + + +//----------------------------------------------------------------------- +// +// dll_symbol +// +dll_symbol: @value type = { + value: std::string = (); + + constant_prefix: std::string_view == "cpp2_metafunction_"; + reachable_mangle: std::string_view == "r"; + const_metafunction_mangle: std::string_view == "c"; + + operator=: (implicit out this) = { } + operator=: (out this, n: declaration_node, is_reachable_: bool) + pre(n.is_metafunction()) + post(is_reachable() == is_reachable_) + post(is_const_metafunction() == n.is_const_metafunction()) + = { + value = constant_prefix; + if is_reachable_ { + value += reachable_mangle; + } + if n.is_const_metafunction() { + value += const_metafunction_mangle; + } + value += mangle(n); + } + operator=: (out this, copy s: std::string_view) + = { + if !s.starts_with(constant_prefix) { + value += constant_prefix; + } + value += s; + } + + c_str: (this) -> *const char = value.c_str(); + view: (this) -> std::string_view = value; + without_prefix: (this) + -> std::string_view + = { + res := view(); + res.remove_prefix(constant_prefix.size()); + for (reachable_mangle, const_metafunction_mangle) + do (m) + if res.starts_with(m) { + res.remove_prefix(m.size()); + } + return res; + } + + is_reachable: (this) -> bool = view().substr(constant_prefix.size()).starts_with(reachable_mangle); + is_const_metafunction: (this) -> bool = + view().substr(constant_prefix.size()) + .substr(unsigned(is_reachable()) * reachable_mangle.size()) + .starts_with(const_metafunction_mangle); +} + +symbols_accessor: () + -> dll_symbol + = dll_symbol("get_symbol_names"); + + +//----------------------------------------------------------------------- +// +// expected with diagnostic to return to apply_metafunctions +// +diagnostic: @struct type = { + value: std::string; +} + +expected: @union type = { + value: T; + error: diagnostic; + + operator=: (implicit out this, v: T) = set_value(v); + operator=: (implicit out this, u: diagnostic) = set_error(u); + + and_then: (move this, f: F) -> std::remove_cvref_t> = { + if is_value() { + return f(value()); + } + else { + return (error()); + } + } +} + +} + +} + + +namespace cpp2::meta { + + +//----------------------------------------------------------------------- +// +// (de)mangling +// +auto mangle(std::string res) + -> std::string +{ + // Mangle (id length, id) pairs according to + // https://en.wikipedia.org/wiki/Name_mangling#Complex_example + // "... by the GNU GCC 3.x compilers, according to the IA-64 (Itanium) ABI" + auto xpos = res.size(); + auto prev_id_end = xpos; + while ((xpos = res.find_last_of(':', xpos)) != res.npos) + { + res.replace(xpos - 1, 2, std::to_string(prev_id_end - xpos - 1)); + prev_id_end = xpos - 1; + } + + return res; +} + +auto mangle(declaration_node const& n) + -> std::string +{ + assert(n.identifier); + assert(n.identifier->template_arguments().empty()); + assert(n.parent_is_namespace()); + + return mangle(n.fully_qualified_name()); +} + +auto demangle(std::string_view s) + -> std::string +{ + std::string res{}; + + // Demangle (id length, id) pairs according to + // https://en.wikipedia.org/wiki/Name_mangling#Complex_example + // "... by the GNU GCC 3.x compilers, according to the IA-64 (Itanium) ABI" + while (!s.empty()) + { + auto length = s.substr(0, s.find_first_not_of("0123456789")); + s.remove_prefix(length.size()); + auto id = s.substr(0, unsafe_narrow(std::stoi(std::string{length}))); + s.remove_prefix(id.size()); + + assert(!length.empty()); + assert(!id.empty()); + + res += id; + if (!s.empty()) { + res += "::"; + } + } + + return res; +} + + +//----------------------------------------------------------------------- +// +// dll Load DLL and its symbols with the OS API +// +class dll +{ +public: + dll(std::string const& path) + : handle_{ +#ifdef _WIN32 + static_cast(LoadLibraryA(path.c_str())) +#else + static_cast(dlopen(path.c_str(), RTLD_NOW|RTLD_LOCAL)) +#endif // _WIN32 + , +[](void* handle) + { +#ifdef _WIN32 + FreeLibrary(static_cast(handle)); +#else + dlclose(handle); +#endif // _WIN32 + } + } + { + if(!handle_) { + Default.report_violation(("failed to load DLL '" + path + "': " + get_last_error()).c_str()); + } + } + + template + auto get_alias(std::string const& name) noexcept -> T& + { +#ifdef _WIN32 + auto symbol = reinterpret_cast(GetProcAddress(static_cast(handle_.get()), name.c_str())); +#else + auto symbol = dlsym(handle_.get(), name.c_str()); + if(!symbol) + { + // Some platforms export with additional underscore, so try that + auto const us_name = "_" + name; + symbol = dlsym(handle_.get(), us_name.c_str()); + } +#endif // _WIN32 + if (!symbol) { + Default.report_violation(("failed to load DLL symbol '" + name+ "': " + get_last_error()).c_str()); + } + return **reinterpret_cast(symbol); + } +private: + std::shared_ptr handle_; + + static auto get_last_error() noexcept -> std::string + { +#ifdef _WIN32 + DWORD errorMessageID = GetLastError(); + if(errorMessageID == 0) { + return {}; // No error message has been recorded + } + LPSTR messageBuffer = nullptr; + auto size = FormatMessageA( + FORMAT_MESSAGE_ALLOCATE_BUFFER|FORMAT_MESSAGE_FROM_SYSTEM|FORMAT_MESSAGE_IGNORE_INSERTS, + nullptr, + errorMessageID, + MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US), + (LPSTR)&messageBuffer, + 0, + nullptr + ); + std::string message(messageBuffer, unsafe_narrow(size)); + LocalFree(messageBuffer); + return message; +#else + return std::string{dlerror()}; +#endif // _WIN32 + } + +}; + + +//----------------------------------------------------------------------- +// +// get_reachable_metafunction_symbols +// +struct library +{ + std::string_view name; + std::vector<::cpp2::meta::record> symbols; +}; + +namespace this_execution { + +// Load Cpp2 libraries with metafunctions by opening DLL with the OS API +// +// The environment variable 'CPPFRONT_METAFUNCTION_LIBRARIES' +// is read and interpreted as ':'-separated Cpp2 metafunction library paths +std::span get_reachable_metafunction_symbols() +{ + static std::vector res = []{ + std::vector res; + + // FIXME: On Windows, using this approach with the system apis not set to utf8, will + // break if a metafunction library contains unicode codepoints in its name, a proper + // way to handle this would be to use _wgetenv and use wchar_t strings for the dll opening + // function + auto cpp1_libraries_cstr = std::getenv("CPPFRONT_METAFUNCTION_LIBRARIES"); + if ( + !cpp1_libraries_cstr + || cpp1_libraries_cstr[0] == '\0' + ) + { + return res; + } + + auto cpp1_libraries = std::string_view{cpp1_libraries_cstr}; + while (!cpp1_libraries.empty()) + { + auto colon = cpp1_libraries.find(':'); + auto lib_path = cpp1_libraries.substr(0, colon); + cpp1_libraries.remove_prefix(lib_path.size() + unsigned(colon != lib_path.npos)); + + // TODO: Ask Johel why are DLLs now loaded into perpetuity? + auto lib = dll{std::string{lib_path}}; + + auto get_symbols = lib.get_alias("cpp2_meta_registry"); + + size_t registry_size; + auto* registry_data = static_cast<::cpp2::meta::record*>(get_symbols(®istry_size)); + + res.push_back({lib_path, {}}); + if (registry_size == 0) { + Default.report_violation( + ("symbols accesor returns no symbols (in '" + std::string{lib_path} + "')").c_str() + ); + } + + for (size_t i = 0; i < registry_size; i++) { + auto symbol = res.back().symbols.emplace_back(registry_data[i]); + } + } + + return res; + }(); + + return res; +} + +} + + +//----------------------------------------------------------------------- +// +// load_metafunction +// +struct lookup_res { + std::string_view library; + ::cpp2::meta::record const* symbol; +}; + +using load_metafunction_ret = std::function; + +// Load Cpp2 metafunction by opening DLL with the OS API +auto load_metafunction( + std::string const& name, + std::function(std::string const&)> lookup + ) + -> expected +{ + return lookup(name).and_then( + [](lookup_res res) + -> expected + { + auto [lib_path, record] = res; + + // TODO: Ask Johel why the double load if we never unload? + auto lib = dll{std::string(lib_path)}; + assert(record->function.index() == 1); + return load_metafunction_ret{ + [ + fun = std::get<1>(record->function), + lib = std::move(lib) + ] + (type_declaration& t) + -> void + { + fun(t); + } + }; + } + ); +} + +} + + +cpp2: namespace = { + +meta: namespace = { + + +//----------------------------------------------------------------------- +// +// Compiler services data +// +//----------------------------------------------------------------------- +// + +compiler_services_data: @struct type = +{ + // Common data members + // + errors : *std::vector; + errors_original_size : int; + generated_tokens : *std::deque; + parser : cpp2::parser; + metafunction_name : std::string = (); + metafunction_args : std::vector = (); + metafunctions_used : bool = false; + + // Make function + // + make: ( + errors_ : *std::vector, + generated_tokens_ : *std::deque, + translation_unit_has_interface: bool + ) + -> compiler_services_data + = { + return (errors_, + cpp2::unsafe_narrow(std::ssize(errors_*)), + generated_tokens_, + cpp2::parser(errors_*, translation_unit_has_interface)); + } +} + + +//----------------------------------------------------------------------- +// +// apply_metafunctions +// +apply_metafunctions: ( + inout n : declaration_node, + inout rtype : type_declaration, + error, + lookup + ) + -> bool += { + assert( n.is_type() ); + + // Check for _names reserved for the metafunction implementation + for rtype.get_members() + do (m) + { + m.require( !m.name().starts_with("_") || m.name().ssize() > 1, + "a type that applies a metafunction cannot have a body that declares a name that starts with '_' - those names are reserved for the metafunction implementation"); + } + + // For each metafunction, apply it + for n.metafunctions + do (meta) + { + // Convert the name and any template arguments to strings + // and record that in rtype + name := meta*.to_string(); + name = name.substr(0, name.find('<')); + + args: std::vector = (); + for meta*.template_arguments() + do (arg) + args.push_back( arg.to_string() ); + + rtype.set_metafunction_name( name, args ); + + // Dispatch + // + if name == "interface" { + interface( rtype ); + } + else if name == "polymorphic_base" { + polymorphic_base( rtype ); + } + else if name == "ordered" { + ordered( rtype ); + } + else if name == "weakly_ordered" { + weakly_ordered( rtype ); + } + else if name == "partially_ordered" { + partially_ordered( rtype ); + } + else if name == "copyable" { + copyable( rtype ); + } + else if name == "basic_value" { + basic_value( rtype ); + } + else if name == "value" { + value( rtype ); + } + else if name == "weakly_ordered_value" { + weakly_ordered_value( rtype ); + } + else if name == "partially_ordered_value" { + partially_ordered_value( rtype ); + } + else if name == "struct" { + cpp2_struct( rtype ); + } + else if name == "enum" { + cpp2_enum( rtype ); + } + else if name == "flag_enum" { + flag_enum( rtype ); + } + else if name == "union" { + cpp2_union( rtype ); + } + else if name == "print" { + print( rtype ); + } + else if name == "dll_visible" { + dll_visible( rtype ); + } + else { + (load := load_metafunction( name, lookup )) + if load.is_value() { + load.value()( rtype ); + } else { + error( "unrecognized metafunction name: " + name ); + if name.find("::") == name.npos { + error( "currently supported built-in names are: interface, polymorphic_base, ordered, weakly_ordered, partially_ordered, copyable, basic_value, value, weakly_ordered_value, partially_ordered_value, struct, enum, flag_enum, union, print, dll_visible" ); + } + if !load.error().value.empty() { + error( load.error().value ); + } + return false; + } + } + + if ( + !args.empty() + && !rtype.arguments_were_used() + ) + { + error( name + " did not use its template arguments - did you mean to write '" + name + " <" + args[0] + "> type' (with the spaces)?"); + return false; + } + } + + return true; +} + + +apply_metafunctions: ( + inout n : declaration_node, + inout rfunction : function_declaration, + error + ) + -> bool += { + assert( n.is_function() ); + + // Check for _names reserved for the metafunction implementation +// for rfunction.get_members() +// do (m) +// { +// m.require( !m.name().starts_with("_") || m.name().ssize() > 1, +// "a function that applies a metafunction cannot have a body that declares a name that starts with '_' - those names are reserved for the metafunction implementation"); +// } + + // For each metafunction, apply it + for n.metafunctions + do (meta) + { + // Convert the name and any template arguments to strings + // and record that in rfunction + name := meta*.to_string(); + name = name.substr(0, name.find('<')); + + args: std::vector = (); + for meta*.template_arguments() + do (arg) + args.push_back( arg.to_string() ); + + rfunction.set_metafunction_name( name, args ); + + // Dispatch + // + if name == "dll_visible" { + dll_visible( rfunction ); + } + else { +// (load := load_metafunction(name)) +// if load.metafunction { +// load.metafunction( rfunction ); +// } else { + error( "unrecognized metafunction name: " + name ); + error( "currently supported built-in names are: dll_visible" ); +// if !load.error.empty() { +// error( load.error ); +// } +// return false; +// } + } + + if ( + !args.empty() + && !rfunction.arguments_were_used() + ) + { + error( name + " did not use its template arguments - did you mean to write '" + name + " <" + args[0] + "> (...) -> ...' (with the spaces)?"); + return false; + } + } + + return true; +} + + +} + +} + + +#include "cpp2reflect.hpp" diff --git a/source/sema.h b/source/sema.h index d339659cd..44fbf913d 100644 --- a/source/sema.h +++ b/source/sema.h @@ -18,23 +18,159 @@ #ifndef CPP2_SEMA_H #define CPP2_SEMA_H -#include "reflect.h" +#include "reflect_impl.h" namespace cpp2 { +using current_declarations_span = std::span; + +auto lookup_metafunction( + std::string const& name, + current_declarations_span current_declarations, + current_names_span current_names + ) + -> meta::expected +{ + struct scope_t { + std::string fully_qualified_mangled_name; + current_names_span::pointer names_first; + current_names_span::pointer names_last = names_first; + + current_names_span names() const { return {names_first, names_last}; } + }; + std::vector scopes = { { {}, current_names.data() + current_names.size() } }; + + // Build up 'scopes' + assert(current_declarations.back()->is_type()); + for ( + auto first = current_declarations.data() + current_declarations.size() - 2, + last = current_declarations.data(); + first != last; + --first + ) + { + assert(*first && (*first)->is_namespace()); + + // TODO Handle unnamed namespace '_' + auto id = (*first)->name(); + assert(id); + for (auto& scope : scopes) + { + scope.fully_qualified_mangled_name.insert(0u, *id); + scope.fully_qualified_mangled_name.insert(0u, "::"); + } + do { + --scopes.back().names_first; + } while ( + !get_if(scopes.back().names_first) + || get(*scopes.back().names_first) != *first + ); + scopes.push_back( { {}, scopes.back().names_first } ); + } + scopes.back().names_first = current_names.data(); + for (auto& scope : scopes) { + scope.fully_qualified_mangled_name = meta::mangle(std::move(scope.fully_qualified_mangled_name)); + } + + // Lookup the name + auto libraries = meta::this_execution::get_reachable_metafunction_symbols(); + auto qualified_name = (name.starts_with("::") ? "" : "::") + name; + auto mangled_name = meta::mangle((name.starts_with("::") ? "" : "::") + name); + for (auto const& scope : scopes) + { + auto expected_symbol = scope.fully_qualified_mangled_name + mangled_name; + // FIXME #470 or emit using statement only in + // Phase 2 "Cpp2 type definitions and function declarations" + // if (auto lookup = source_order_name_lookup(scope.names(), name)) { + // if (auto using_ = get_if(&*lookup)) { + // // TODO Handle relative qualified-id + // expected_symbol = meta::mangle((name.starts_with("::") ? "" : "::") + using_->to_string()); + // } + // } + + // Save a const overload + auto res = meta::lookup_res{}; + for (auto&& lib: libraries) + { + for (auto&& sym: lib.symbols) + { + if (sym.fully_qualified_name == qualified_name) + { + res = {lib.name, &sym}; + // Immediately return a non-const overload + return res; + } + } + } + + // Return a const overload without a non-const overload + if (!res.library.empty()) { + return res; + } + } + + return meta::diagnostic{ + "metafunction '" + name + "' not found\n" + + "(temporary alpha limitation) lookup for a metafunction name is limited: " + + "it can be used unqualified from its declaring namespace or a nested namespace thereof, " + + "and otherwise requires full qualification" + }; +} + auto parser::apply_type_metafunctions( declaration_node& n ) -> bool { assert(n.is_type()); // Get the reflection state ready to pass to the function - auto cs = meta::compiler_services{ &errors, generated_tokens }; + auto cs = meta::compiler_services{ + meta::compiler_services_data::make( &errors, generated_tokens, translation_unit_has_interface() ) + }; auto rtype = meta::type_declaration{ &n, cs }; return apply_metafunctions( - n, + n, rtype, + [&](std::string const& msg) { error( msg, false ); }, + [&](std::string const& name) { + auto res = lookup_metafunction(name, current_declarations, current_names); + + // Save sanity check to ensure the Cpp1 lookup matches ours + // if ( + // res.is_value() + // && res.value().symbol->is_reachable() + // ) + // { + // auto check = std::string{}; + // check += "static_assert("; + // check += meta::to_type_metafunction_cast(name, res.value().symbol->is_const_metafunction()); + // check += " == "; + // check += res.value().symbol->view(); + // check += ", "; + // check += "\"the metafunction name '" + name + "' must be "; + // check += "reachable and equal to the one evaluated by cppfront\""; + // check += ");\n"; + // n.metafunction_lookup_checks.push_back(check); + // } + + return res; + } + ); +} + +auto parser::apply_function_metafunctions( declaration_node& n ) + -> bool +{ + assert(n.is_function()); + + // Get the reflection state ready to pass to the function + auto cs = meta::compiler_services{meta::compiler_services_data::make( &errors, generated_tokens, false )}; + auto rfunction = meta::function_declaration{ &n, cs }; + + return apply_metafunctions( + n, + rfunction, [&](std::string const& msg) { error( msg, false ); } ); } @@ -668,9 +804,9 @@ class sema // and this isn't generated code (ignore that for now) // and this is a user-named object (not 'this', 'that', or '_') if ( - i == pos - && id->position().lineno > 0 - && *id != "this" + i == pos + && id->position().lineno > 0 + && *id != "this" && *id != "that" && *id != "_" ) @@ -1741,7 +1877,7 @@ class sema // Skip type scope (member) variables && !(n.parent_is_type() && n.is_object()) // Skip unnamed variables - && n.identifier + && n.identifier // Skip non-out parameters && ( !inside_parameter_list diff --git a/source/to_cpp1.h b/source/to_cpp1.h index d4cfd47c1..e586283a7 100644 --- a/source/to_cpp1.h +++ b/source/to_cpp1.h @@ -1016,6 +1016,12 @@ class cppfront std::string sourcefile; std::vector errors; + struct metafunction_symbol { + std::string fully_qualified_name; + bool is_const; // FIXME: Could be passing-style directly instead. + }; + std::vector metafunction_symbols; + // For building // cpp2::source source; @@ -1044,19 +1050,6 @@ class cppfront }; std::vector current_args = { {} }; - struct active_using_declaration { - token const* identifier = {}; - - explicit active_using_declaration(using_statement_node const& n) { - if (auto id = get_if(&n.id->id)) { - identifier = (*id)->ids.back().id->identifier; - } - } - }; - - using source_order_name_lookup_res = - std::optional>; - // Stack of the currently active nested declarations we're inside std::vector current_declarations = { {} }; @@ -1166,7 +1159,7 @@ class cppfront : sourcefile{ filename } , source { errors } , tokens { errors } - , parser { errors } + , parser { errors, sourcefile.ends_with(".h2") } , sema { errors } { // "Constraints enable creativity in the right directions" @@ -1479,7 +1472,7 @@ class cppfront // If there is Cpp2 code, we have more to do... - // First, if this is a .h2 and in a -pure-cpp2 compilation, + // If this is a .h2 and in a -pure-cpp2 compilation, // we need to switch filenames if ( cpp1_filename.back() == 'h' @@ -1542,6 +1535,25 @@ class cppfront printer.finalize_phase( true ); + //--------------------------------------------------------------------- + // Emit the accessor for declared metafunctions + // + if (!metafunction_symbols.empty()) + { + assert(source.has_cpp2()); + + printer.print_extra("namespace { // Metafunctions registry\n"); + int reg_count = 0; + for (auto const& mf: metafunction_symbols) { + auto reg = std::string{"cpp2::meta::register_function "}; + reg += "rf" + std::to_string(++reg_count) + "("; + reg += "\"" + mf.fully_qualified_name + "\","; + reg += "std::addressof(" + mf.fully_qualified_name + "));\n"; + printer.print_extra(reg); + } + printer.print_extra("}\n"); + } + // Finally, some debug checks assert( (!errors.empty() || tokens.num_unprinted_comments() == 0) @@ -2511,7 +2523,8 @@ class cppfront if (n.for_namespace) { printer.print_cpp2(" namespace", n.position()); - } else { + } + else { current_names.push_back(active_using_declaration{n}); } @@ -2815,39 +2828,6 @@ class cppfront } - auto source_order_name_lookup(unqualified_id_node const& id) - -> source_order_name_lookup_res - { - for ( - auto first = current_names.rbegin(), last = current_names.rend() - 1; - first != last; - ++first - ) - { - if ( - auto decl = get_if(&*first); - decl - && *decl - && (*decl)->has_name(*id.identifier) - ) - { - return *decl; - } - else if ( - auto using_ = get_if(&*first); - using_ - && using_->identifier - && *using_->identifier == *id.identifier - ) - { - return *using_; - } - } - - return {}; - } - - auto lookup_finds_variable_with_placeholder_type_under_initialization(id_expression_node const& n) -> bool { @@ -2857,7 +2837,7 @@ class cppfront } auto const& id = *get(n.id); - auto lookup = source_order_name_lookup(id); + auto lookup = source_order_name_lookup(current_names, *id.identifier); if ( !lookup @@ -3970,51 +3950,6 @@ class cppfront } - // Consider moving these `stack` functions to `common.h` to enable more general use. - - template - auto stack_value( - T& var, - std::type_identity_t const& value - ) - -> auto - { - return finally([&var, old = std::exchange(var, value)]() { - var = old; - }); - } - - template - auto stack_element( - std::vector& cont, - std::type_identity_t const& value - ) - -> auto - { - cont.push_back(value); - return finally([&]{ cont.pop_back(); }); - } - - template - auto stack_size(std::vector& cont) - -> auto - { - return finally([&, size = cont.size()]{ cont.resize(size); }); - } - - template - auto stack_size_if( - std::vector& cont, - bool cond - ) - -> std::optional - { - if (cond) { - return stack_size(cont); - } - return {}; - } - //----------------------------------------------------------------------- // auto emit( @@ -4319,10 +4254,11 @@ class cppfront && !type_id.is_pointer_qualified() ) { - switch (n.pass) { - break;case passing_style::in : printer.print_cpp2( "cpp2::in<", n.position() ); - break;case passing_style::out : printer.print_cpp2( "cpp2::out<", n.position() ); - break;default: ; + if (n.pass == passing_style::in) { + printer.print_cpp2( "cpp2::in<", n.position() ); + } + else if (n.pass == passing_style::out) { + printer.print_cpp2( "cpp2::out<", n.position() ); } } @@ -4360,22 +4296,29 @@ class cppfront function_requires_conditions.push_back(req); } - switch (n.pass) { - break;case passing_style::in : printer.print_cpp2( name+" const&", n.position() ); - break;case passing_style::copy : printer.print_cpp2( name, n.position() ); - break;case passing_style::inout : printer.print_cpp2( name+"&", n.position() ); - + if (n.pass == passing_style::in) { + printer.print_cpp2( name+" const&", n.position() ); + } + else if (n.pass == passing_style::copy) { + printer.print_cpp2( name, n.position() ); + } + else if (n.pass == passing_style::inout) { + printer.print_cpp2( name+"&", n.position() ); + } // For generic out parameters, we take a pointer to anything with paramater named "identifier_" // and then generate the out<> as a stack local with the expected name "identifier" - break;case passing_style::out : printer.print_cpp2( name, n.position() ); - current_functions.back().prolog.statements.push_back( - "auto " + identifier + " = cpp2::out(" + identifier + "_); " - ); - identifier += "_"; - - break;case passing_style::move : printer.print_cpp2( name+"&&", n.position() ); - break;case passing_style::forward: printer.print_cpp2( name+"&&", n.position() ); - break;default: ; + else if (n.pass == passing_style::out) { + printer.print_cpp2( name, n.position() ); + current_functions.back().prolog.statements.push_back( + "auto " + identifier + " = cpp2::out(" + identifier + "_); " + ); + identifier += "_"; + } + else if (n.pass == passing_style::move) { + printer.print_cpp2( name+"&&", n.position() ); + } + else if (n.pass == passing_style::forward) { + printer.print_cpp2( name+"&&", n.position() ); } } else if (n.pass == passing_style::forward) { @@ -4411,14 +4354,23 @@ class cppfront && !n.declaration->is_variadic ) { - switch (n.pass) { - break;case passing_style::in : printer.print_cpp2( ">", n.position() ); - break;case passing_style::copy : printer.print_cpp2( "", n.position() ); - break;case passing_style::inout : printer.print_cpp2( "&", n.position() ); - break;case passing_style::out : printer.print_cpp2( ">", n.position() ); - break;case passing_style::move : printer.print_cpp2( "&&", n.position() ); - break;case passing_style::forward: printer.print_cpp2( "&&", n.position() ); - break;default: ; + if (n.pass == passing_style::in) { + printer.print_cpp2( ">", n.position() ); + } + else if (n.pass == passing_style::copy) { + printer.print_cpp2( "", n.position() ); + } + else if (n.pass == passing_style::inout) { + printer.print_cpp2( "&", n.position() ); + } + else if (n.pass == passing_style::out) { + printer.print_cpp2( ">", n.position() ); + } + else if (n.pass == passing_style::move) { + printer.print_cpp2( "&&", n.position() ); + } + else if (n.pass == passing_style::forward) { + printer.print_cpp2( "&&", n.position() ); } } @@ -4639,7 +4591,7 @@ class cppfront ); } else { - emit(*n.parameters, false, false, generating_postfix_inc_dec); + emit(*n.parameters, false, false, generating_postfix_inc_dec); } // For an anonymous function, the emitted lambda is 'constexpr' or 'mutable' @@ -5570,6 +5522,14 @@ class cppfront } + // Emit sanity check to ensure the Cpp1 lookup matches ours + if (printer.get_phase() == printer.phase1_type_defs_func_decls) { + for (auto&& check : n.metafunction_lookup_checks) { + printer.print_extra(check); + } + } + + // If this is a function that has multiple return values, // first we need to emit the struct that contains the returns if ( @@ -5679,6 +5639,17 @@ class cppfront } } + // If this is a function declaration visible in a DLL + // emit the portable macro that makes it visible + if ( + printer.get_phase() != printer.phase0_type_decls + && n.is_function() + && n.is_dll_visible() + ) + { + printer.print_cpp2("CPPFRONTAPI ", n.position()); + } + // If this is a function definition and the function is inside // type(s) that have template parameters and/or requires clauses, // emit those outer template parameters and requires clauses too @@ -5733,6 +5704,17 @@ class cppfront } printer.print_cpp2("class ", n.position()); + + // If this is a declaration visible in a DLL + // emit the portable macro that makes it visible + if ( + printer.get_phase() == printer.phase1_type_defs_func_decls + && n.is_dll_visible() + ) + { + printer.print_cpp2("CPPFRONTAPI ", n.position()); + } + emit(*n.identifier); // Type declaration @@ -6050,28 +6032,32 @@ class cppfront assert (is_in_type); auto& this_ = func->parameters->parameters[0]; - switch (this_->pass) { - break;case passing_style::in: + if (this_->pass == passing_style::in) { suffix1 += " const"; // Cpp1 ref-qualifiers don't belong on virtual functions if (!this_->is_polymorphic()) { suffix1 += "&"; } - break;case passing_style::inout: + } + else if (this_->pass == passing_style::inout) { // Cpp1 ref-qualifiers don't belong on virtual functions if (!this_->is_polymorphic()) { suffix1 += " &"; } - break;case passing_style::out: - ; // constructor is handled below - break;case passing_style::move: + } + else if (this_->pass == passing_style::out) { + // constructor is handled below + } + else if (this_->pass == passing_style::move) { suffix1 += " &&"; - + } // We shouldn't be able to get into a state where these values // exist here, if we did it's our compiler bug - break;case passing_style::copy: - case passing_style::forward: - default: + else if (this_->pass == passing_style::copy + || this_->pass == passing_style::forward + || this_->pass == passing_style::invalid + ) + { errors.emplace_back( n.position(), "ICE: invalid parameter passing style, should have been rejected", true); } @@ -6311,6 +6297,14 @@ class cppfront printer.print_cpp2( ";", n.position() ); } + // Save the symbol for a metafunction + // to be emitted in the global namespace and + // to be loaded by `cpp2::meta::load_metafunction` + if (n.is_metafunction()) + { + metafunction_symbols.push_back({n.fully_qualified_name(), n.is_const_metafunction()}); + } + // Note: Not just early "return;" here because we may need // to recurse to emit generated operator declarations too, // so all the definition work goes into a big 'else' branch