// Copyright (C) 2020-2024 Free Software Foundation, Inc.
// This file is part of GCC.
// GCC is free software; you can redistribute it and/or modify it under
// the terms of the GNU General Public License as published by the Free
// Software Foundation; either version 3, or (at your option) any later
// version.
// GCC is distributed in the hope that it will be useful, but WITHOUT ANY
// WARRANTY; without even the implied warranty of MERCHANTABILITY or
// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
// for more details.
// You should have received a copy of the GNU General Public License
// along with GCC; see the file COPYING3. If not see
// .
// #include "rust-session-manager.h"
#ifndef RUST_SESSION_MANAGER_H
#define RUST_SESSION_MANAGER_H
#include "rust-linemap.h"
#include "rust-backend.h"
#include "rust-hir-map.h"
#include "safe-ctype.h"
#include "config.h"
#include "rust-system.h"
#include "coretypes.h"
#include "options.h"
#include "optional.h"
namespace Rust {
// parser forward decl
template class Parser;
class Lexer;
// crate forward decl
namespace AST {
struct Crate;
}
// crate forward decl
namespace HIR {
class Crate;
}
/* Data related to target, most useful for conditional compilation and
* whatever. */
struct TargetOptions
{
/* TODO: maybe make private and access through helpers to allow changes to
* impl */
std::unordered_map>>
features;
enum class CrateType
{
BIN = 0,
LIB,
RLIB,
DYLIB,
CDYLIB,
STATICLIB,
PROC_MACRO
} crate_type
= CrateType::BIN;
public:
void set_crate_type (int raw_type)
{
crate_type = static_cast (raw_type);
}
const CrateType &get_crate_type () const { return crate_type; }
// Returns whether a key is defined in the feature set.
bool has_key (std::string key) const
{
auto it = features.find (key);
return it != features.end ()
&& it->second.find (tl::nullopt) != it->second.end ();
}
// Returns whether a key exists with the given value in the feature set.
bool has_key_value_pair (std::string key, std::string value) const
{
auto it = features.find (key);
if (it != features.end ())
{
auto set = it->second;
auto it2 = set.find (value);
if (it2 != set.end ())
return true;
}
return false;
}
/* Returns the singular value from the key, or if the key has multiple, an
* empty string. */
std::string get_singular_value (std::string key) const
{
auto it = features.find (key);
if (it != features.end ())
{
auto set = it->second;
if (set.size () == 1 && set.begin ()->has_value ())
return set.begin ()->value ();
}
return "";
}
/* Returns all values associated with a key (including none), or an empty
* set if no key is found. */
std::unordered_set get_values_for_key (std::string key) const
{
std::unordered_set ret;
auto it = features.find (key);
if (it == features.end ())
return {};
for (auto &val : it->second)
if (val.has_value ())
ret.insert (val.value ());
return ret;
}
/* Inserts a key (no value) into the feature set. This will do nothing if
* the key already exists. This returns whether the insertion was successful
* (i.e. whether key already existed). */
bool insert_key (std::string key)
{
auto it = features.find (key);
if (it == features.end ())
it
= features
.insert (
std::make_pair (std::move (key),
std::unordered_set> ()))
.first;
return it->second.insert (tl::nullopt).second;
}
// Inserts a key-value pair into the feature set.
void insert_key_value_pair (std::string key, std::string value)
{
auto it = features.find (key);
if (it == features.end ())
it
= features
.insert (
std::make_pair (std::move (key),
std::unordered_set> ()))
.first;
it->second.insert (std::move (value));
}
// Dump all target options to stderr.
void dump_target_options () const;
/* Creates derived values and implicit enables after all target info is
* added (e.g. "unix"). */
void init_derived_values ();
/* Enables all requirements for the feature given, and will enable feature
* itself if not enabled. */
void enable_implicit_feature_reqs (std::string feature);
/* According to reference, Rust uses either multi-map key-values or just
* values (although values may be aliases for a key-value value). This seems
* like overkill. Thus, depending on whether the attributes used in cfg are
* fixed or not, I think I'll either put each non-multimap "key-value" as a
* separate field and have the multimap "key-values" in a regular map for
* that one key, or actually use a multimap.
*
* rustc itself uses a set of key-value tuples where the second tuple
* element is optional. This gets rid of the requirement to make a
* multi-map, I guess, but seems like it might make search slow (unless all
* "is defined"-only ones have empty string as second element). */
/* cfg attributes:
* - target_arch: single value
* - target_feature: multiple values possible
* - target_os: single value
* - target_family: single value (or no value?)
* - unix: set when target_family = "unix"
* - windows: set when target_family = "windows"
* - if these are just syntactic sugar, then maybe have a separate set or
* map for this kind of stuff
* - target_env: set when needed for disambiguation about ABI - usually
* empty string for GNU, complicated
* - seems to be a single value (if any)
* - target_endian: single value; "little" or "big"
* - target_pointer_width: single value, "32" for 32-bit pointers, etc.
* - target_vendor, single value
* - test: set when testing is being done
* - again, seems similar to a "is defined" rather than "is equal to" like
* unix
* - debug_assertions: seems to "is defined"
* - proc_macro: no idea, bad docs. seems to be boolean, so maybe "is
* defined"
*/
};
// Defines compiler options (e.g. dump, etc.).
struct CompileOptions
{
enum DumpOption
{
LEXER_DUMP,
AST_DUMP_PRETTY,
REGISTER_PLUGINS_DUMP,
INJECTION_DUMP,
EXPANSION_DUMP,
RESOLUTION_DUMP,
TARGET_OPTION_DUMP,
HIR_DUMP,
HIR_DUMP_PRETTY,
BIR_DUMP,
};
std::set dump_options;
/* configuration options - actually useful for conditional compilation and
* whatever data related to target arch, features, os, family, env, endian,
* pointer width, vendor */
TargetOptions target_data;
std::string crate_name;
bool crate_name_set_manually = false;
bool enable_test = false;
bool debug_assertions = false;
std::string metadata_output_path;
enum class Edition
{
E2015 = 0,
E2018,
E2021,
} edition
= Edition::E2015;
enum class CompileStep
{
Ast,
AttributeCheck,
Expansion,
ASTValidation,
FeatureGating,
NameResolution,
Lowering,
TypeCheck,
Privacy,
Unsafety,
Const,
BorrowCheck,
Compilation,
End,
} compile_until
= CompileStep::End;
bool dump_option_enabled (DumpOption option) const
{
return dump_options.find (option) != dump_options.end ();
}
void enable_dump_option (DumpOption option) { dump_options.insert (option); }
void enable_all_dump_options ()
{
enable_dump_option (DumpOption::LEXER_DUMP);
enable_dump_option (DumpOption::AST_DUMP_PRETTY);
enable_dump_option (DumpOption::REGISTER_PLUGINS_DUMP);
enable_dump_option (DumpOption::INJECTION_DUMP);
enable_dump_option (DumpOption::EXPANSION_DUMP);
enable_dump_option (DumpOption::RESOLUTION_DUMP);
enable_dump_option (DumpOption::TARGET_OPTION_DUMP);
enable_dump_option (DumpOption::HIR_DUMP);
enable_dump_option (DumpOption::HIR_DUMP_PRETTY);
enable_dump_option (DumpOption::BIR_DUMP);
}
void set_crate_name (std::string name)
{
rust_assert (!name.empty ());
crate_name = std::move (name);
}
const std::string &get_crate_name () const
{
rust_assert (!crate_name.empty ());
return crate_name;
}
void set_edition (int raw_edition)
{
edition = static_cast (raw_edition);
}
const Edition &get_edition () const { return edition; }
void set_crate_type (int raw_type) { target_data.set_crate_type (raw_type); }
bool is_proc_macro () const
{
return target_data.get_crate_type ()
== TargetOptions::CrateType::PROC_MACRO;
}
void set_compile_step (int raw_step)
{
compile_until = static_cast (raw_step);
}
const CompileStep &get_compile_until () const { return compile_until; }
void set_metadata_output (const std::string &path)
{
metadata_output_path = path;
}
const std::string &get_metadata_output () const
{
return metadata_output_path;
}
bool metadata_output_path_set () const
{
return !metadata_output_path.empty ();
}
};
/* Defines a compiler session. This is for a single compiler invocation, so
* potentially includes parsing multiple crates. */
struct Session
{
CompileOptions options;
/* This should really be in a per-crate storage area but it is wiped with
* every file so eh. */
std::string injected_crate_name;
std::map extern_crates;
/* extra files get included during late stages of compilation (e.g. macro
* expansion) */
std::vector extra_files;
// backend linemap
Linemap *linemap;
// mappings
Analysis::Mappings *mappings;
public:
/* Get a reference to the static session instance */
static Session &get_instance ();
Session () = default;
~Session () = default;
/* This initializes the compiler session. Corresponds to langhook
* grs_langhook_init(). Note that this is called after option handling. */
void init ();
// delete those constructors so we don't access the singleton in any
// other way than via `get_instance()`
Session (Session const &) = delete;
void operator= (Session const &) = delete;
bool handle_option (enum opt_code code, const char *arg, HOST_WIDE_INT value,
int kind, location_t loc,
const struct cl_option_handlers *handlers);
void handle_input_files (int num_files, const char **files);
void init_options ();
void handle_crate_name (const AST::Crate &parsed_crate);
/* This function saves the filename data into the session manager using the
* `move` semantics, and returns a C-style string referencing the input
* std::string */
inline const char *include_extra_file (std::string filename)
{
extra_files.push_back (std::move (filename));
return extra_files.back ().c_str ();
}
NodeId load_extern_crate (const std::string &crate_name, location_t locus);
private:
void compile_crate (const char *filename);
bool enable_dump (std::string arg);
void dump_lex (Parser &parser) const;
void dump_ast_pretty (AST::Crate &crate, bool expanded = false) const;
void dump_hir (HIR::Crate &crate) const;
void dump_hir_pretty (HIR::Crate &crate) const;
// pipeline stages - TODO maybe move?
/* Register plugins pipeline stage. TODO maybe move to another object?
* Currently dummy stage. In future will handle attribute injection
* (top-level inner attribute creation from command line arguments), setting
* options maybe, registering lints maybe, loading plugins maybe. */
void register_plugins (AST::Crate &crate);
/* Injection pipeline stage. TODO maybe move to another object? Maybe have
* some lint checks (in future, obviously), register builtin macros, crate
* injection. */
void injection (AST::Crate &crate);
/* Expansion pipeline stage. TODO maybe move to another object? Expands all
* macros, maybe build test harness in future, AST validation, maybe create
* macro crate (if not rustdoc).*/
void expansion (AST::Crate &crate);
// handle cfg_option
bool handle_cfg_option (std::string &data);
bool handle_extern_option (std::string &data);
};
} // namespace Rust
#if CHECKING_P
namespace selftest {
extern void
rust_crate_name_validation_test (void);
}
#endif // CHECKING_P
#endif