use std::collections::{BTreeMap, HashMap};
use std::fmt;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum ExplicitObjectKind {
DeducedRef,
DeducedLvalueRef,
DeducedByValue,
DeducedConstLvalueRef,
DeducedConstRefAlt,
}
impl ExplicitObjectKind {
pub fn from_param_str(s: &str) -> Option<Self> {
match s {
"this auto&& self" | "this auto&&" => Some(Self::DeducedRef),
"this auto& self" | "this auto&" => Some(Self::DeducedLvalueRef),
"this auto self" | "this auto" => Some(Self::DeducedByValue),
"this const auto& self" | "this const auto&" => Some(Self::DeducedConstLvalueRef),
"this auto const& self" | "this auto const&" => Some(Self::DeducedConstRefAlt),
_ => None,
}
}
pub fn is_reference(&self) -> bool {
matches!(
self,
Self::DeducedRef
| Self::DeducedLvalueRef
| Self::DeducedConstLvalueRef
| Self::DeducedConstRefAlt
)
}
pub fn is_const(&self) -> bool {
matches!(self, Self::DeducedConstLvalueRef | Self::DeducedConstRefAlt)
}
}
#[derive(Debug, Clone)]
pub struct ExplicitObjectFunction {
pub name: String,
pub explicit_param: ExplicitObjectKind,
pub param_name: String,
pub regular_params: Vec<FunctionParam>,
pub return_type: DeducedReturnType,
pub is_member: bool,
pub is_constexpr: bool,
pub class_name: Option<String>,
pub is_static: bool,
}
#[derive(Debug, Clone, PartialEq)]
pub enum DeducedReturnType {
Auto,
AutoRef,
AutoConstRef,
Explicit(String),
Void,
}
#[derive(Debug, Clone, PartialEq)]
pub struct FunctionParam {
pub name: String,
pub type_desc: String,
pub default_value: Option<String>,
}
impl ExplicitObjectFunction {
pub fn new(name: &str, kind: ExplicitObjectKind, param_name: &str) -> Self {
Self {
name: name.to_string(),
explicit_param: kind,
param_name: param_name.to_string(),
regular_params: Vec::new(),
return_type: DeducedReturnType::Auto,
is_member: false,
is_constexpr: false,
class_name: None,
is_static: false,
}
}
pub fn signature(&self) -> String {
let class_prefix = if let Some(ref cls) = self.class_name {
format!("{}::", cls)
} else {
String::new()
};
let static_prefix = if self.is_static { "static " } else { "" };
let constexpr_prefix = if self.is_constexpr { "constexpr " } else { "" };
let ret = match &self.return_type {
DeducedReturnType::Auto => "auto".to_string(),
DeducedReturnType::AutoRef => "auto&".to_string(),
DeducedReturnType::AutoConstRef => "const auto&".to_string(),
DeducedReturnType::Explicit(t) => t.clone(),
DeducedReturnType::Void => "void".to_string(),
};
let explicit = format!("this auto&& {}", self.param_name);
let params: Vec<String> = std::iter::once(explicit)
.chain(self.regular_params.iter().map(|p| {
if let Some(ref def) = p.default_value {
format!("{} {} = {}", p.type_desc, p.name, def)
} else {
format!("{} {}", p.type_desc, p.name)
}
}))
.collect();
format!(
"{}{}{}{}({})",
constexpr_prefix,
static_prefix,
ret,
format!("{}operator()", class_prefix),
params.join(", ")
)
}
pub fn can_bind_lvalue(&self) -> bool {
match self.explicit_param {
ExplicitObjectKind::DeducedRef
| ExplicitObjectKind::DeducedLvalueRef
| ExplicitObjectKind::DeducedConstLvalueRef
| ExplicitObjectKind::DeducedConstRefAlt => true,
ExplicitObjectKind::DeducedByValue => true,
}
}
pub fn can_bind_rvalue(&self) -> bool {
match self.explicit_param {
ExplicitObjectKind::DeducedRef | ExplicitObjectKind::DeducedByValue => true,
ExplicitObjectKind::DeducedLvalueRef
| ExplicitObjectKind::DeducedConstLvalueRef
| ExplicitObjectKind::DeducedConstRefAlt => false,
}
}
}
pub struct DeducingThisParser {
pub enabled: bool,
pub diagnostics: Vec<DeducingThisDiag>,
}
#[derive(Debug, Clone)]
pub struct DeducingThisDiag {
pub message: String,
pub is_error: bool,
}
impl DeducingThisParser {
pub fn new() -> Self {
Self {
enabled: true,
diagnostics: Vec::new(),
}
}
pub fn parse_param(&mut self, param_str: &str) -> Option<ExplicitObjectKind> {
let trimmed = param_str.trim();
if !trimmed.starts_with("this ") {
return None;
}
ExplicitObjectKind::from_param_str(trimmed)
}
pub fn validate(&mut self, func: &ExplicitObjectFunction) -> bool {
let mut valid = true;
if func.is_static && !func.is_member {
self.diagnostics.push(DeducingThisDiag {
message: format!(
"free function '{}' cannot have an explicit object parameter",
func.name
),
is_error: true,
});
valid = false;
}
if func.name == func.class_name.clone().unwrap_or_default() {
self.diagnostics.push(DeducingThisDiag {
message: "constructors cannot have explicit object parameters".to_string(),
is_error: true,
});
valid = false;
}
if func.name.starts_with('~') {
self.diagnostics.push(DeducingThisDiag {
message: "destructors cannot have explicit object parameters".to_string(),
is_error: true,
});
valid = false;
}
valid
}
pub fn deduce_this_type(&self, func: &ExplicitObjectFunction, call_is_lvalue: bool) -> String {
match func.explicit_param {
ExplicitObjectKind::DeducedRef => {
if call_is_lvalue {
"auto&".to_string()
} else {
"auto&&".to_string()
}
}
ExplicitObjectKind::DeducedLvalueRef => "auto&".to_string(),
ExplicitObjectKind::DeducedByValue => "auto".to_string(),
ExplicitObjectKind::DeducedConstLvalueRef | ExplicitObjectKind::DeducedConstRefAlt => {
"const auto&".to_string()
}
}
}
}
impl Default for DeducingThisParser {
fn default() -> Self {
Self::new()
}
}
pub struct DeducingThisCodeGen {
pub enabled: bool,
}
impl DeducingThisCodeGen {
pub fn new() -> Self {
Self { enabled: true }
}
pub fn emit_call_operator_type(&self, _func: &ExplicitObjectFunction) -> String {
"void (%class.Foo*, i32)*".to_string()
}
pub fn lower_call(&self, object_ptr: &str, args: &[String]) -> String {
format!(
"call void @operator()(ptr noundef {}, i32 {})",
object_ptr,
args.join(", i32 ")
)
}
}
impl Default for DeducingThisCodeGen {
fn default() -> Self {
Self::new()
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum IfConstevalResult {
ImmediateContext,
NonImmediateContext,
}
#[derive(Debug, Clone)]
pub struct ConstevalContext {
stack: Vec<bool>,
depth: usize,
}
impl ConstevalContext {
pub fn new() -> Self {
Self {
stack: Vec::new(),
depth: 0,
}
}
pub fn enter(&mut self, is_consteval: bool) {
self.stack.push(is_consteval);
self.depth += 1;
}
pub fn leave(&mut self) {
if self.depth > 0 {
self.stack.pop();
self.depth -= 1;
}
}
pub fn is_consteval(&self) -> bool {
self.stack.last().copied().unwrap_or(false)
}
pub fn evaluate_if_consteval(&self) -> IfConstevalResult {
if self.is_consteval() {
IfConstevalResult::ImmediateContext
} else {
IfConstevalResult::NonImmediateContext
}
}
}
impl Default for ConstevalContext {
fn default() -> Self {
Self::new()
}
}
pub struct IfConstevalParser {
pub contexts: ConstevalContext,
}
impl IfConstevalParser {
pub fn new() -> Self {
Self {
contexts: ConstevalContext::new(),
}
}
pub fn detect_consteval(&self, tokens: &[&str]) -> bool {
if tokens.len() >= 2 {
return tokens[0] == "if" && tokens[1] == "consteval";
}
false
}
pub fn parse_body(&mut self, is_immediate_context: bool, body: &str) -> String {
self.contexts.enter(is_immediate_context);
let result = body.to_string();
self.contexts.leave();
result
}
}
#[derive(Debug, Clone)]
pub struct StaticOperatorCall {
pub class_name: String,
pub params: Vec<String>,
pub return_type: String,
pub is_lambda: bool,
pub has_captures: bool,
}
impl StaticOperatorCall {
pub fn new(class_name: &str) -> Self {
Self {
class_name: class_name.to_string(),
params: Vec::new(),
return_type: "void".to_string(),
is_lambda: false,
has_captures: false,
}
}
pub fn validate(&self) -> Result<(), String> {
if self.is_lambda {
if self.has_captures {
return Err(
"static lambda cannot have captures (lambda with static operator())".to_string(),
);
}
}
Ok(())
}
pub fn mangled_name(&self) -> String {
format!("_ZN{}clE", self.class_name.len())
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum CallOperatorKind {
Mutable,
Const,
Static,
ExplicitObject,
}
impl fmt::Display for CallOperatorKind {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::Mutable => write!(f, "operator()"),
Self::Const => write!(f, "operator() const"),
Self::Static => write!(f, "static operator()"),
Self::ExplicitObject => write!(f, "operator() [explicit object]"),
}
}
}
#[derive(Debug, Clone)]
pub struct MultiDimSubscript {
pub index_count: usize,
pub index_types: Vec<String>,
pub return_type: String,
pub is_const: bool,
pub source_line: usize,
}
impl MultiDimSubscript {
pub fn new(index_count: usize) -> Self {
Self {
index_count,
index_types: Vec::new(),
return_type: "auto&".to_string(),
is_const: false,
source_line: 0,
}
}
pub fn validate(&self) -> Result<(), Vec<String>> {
let mut errors = Vec::new();
if self.index_count == 0 {
errors.push("operator[] must have at least one parameter".to_string());
}
if self.index_count == 0 {
errors.push("operator[] with zero indices is not allowed".to_string());
}
if errors.is_empty() {
Ok(())
} else {
Err(errors)
}
}
pub fn call_syntax(&self, object: &str, indices: &[&str]) -> String {
format!("{}[{}]", object, indices.join(", "))
}
pub fn lower_subscript(&self, base_ptr: &str, indices: &[i64]) -> String {
let gep_indices: Vec<String> = indices.iter().map(|i| format!("i64 {}", i)).collect();
format!(
"getelementptr inbounds {}, {}, {}",
"i8",
base_ptr,
gep_indices.join(", ")
)
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum DecayCopyKind {
DecayCopyParen,
DecayCopyBrace,
}
impl fmt::Display for DecayCopyKind {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::DecayCopyParen => write!(f, "auto"),
Self::DecayCopyBrace => write!(f, "auto{{}}"),
}
}
}
#[derive(Debug, Clone)]
pub struct DecayCopyExpr {
pub kind: DecayCopyKind,
pub inner_expr: String,
pub deduced_type: String,
pub is_prvalue: bool,
}
impl DecayCopyExpr {
pub fn new(kind: DecayCopyKind, inner: &str) -> Self {
Self {
kind,
inner_expr: inner.to_string(),
deduced_type: String::new(),
is_prvalue: true,
}
}
pub fn emit(&self) -> String {
match self.kind {
DecayCopyKind::DecayCopyParen => format!("auto({})", self.inner_expr),
DecayCopyKind::DecayCopyBrace => format!("auto{{{}}}", self.inner_expr),
}
}
pub fn apply_decay(&self, original_type: &str) -> String {
let mut t = original_type.to_string();
if t.ends_with('&') {
t = t[..t.len() - 1].to_string();
}
if t.ends_with("&&") {
t = t[..t.len() - 2].to_string();
}
if t.starts_with("const ") {
t = t[6..].to_string();
}
if t.starts_with("volatile ") {
t = t[9..].to_string();
}
if t.ends_with(']') {
if let Some(bracket_pos) = t.find('[') {
let base = t[..bracket_pos].trim().to_string();
t = format!("{}*", base);
}
}
t
}
}
pub struct DecayCopyParser;
impl DecayCopyParser {
pub fn try_parse(input: &str) -> Option<DecayCopyExpr> {
let trimmed = input.trim();
if let Some(rest) = trimmed.strip_prefix("auto(") {
if let Some(inner) = rest.strip_suffix(')') {
return Some(DecayCopyExpr::new(DecayCopyKind::DecayCopyParen, inner.trim()));
}
}
if let Some(rest) = trimmed.strip_prefix("auto{") {
if let Some(inner) = rest.strip_suffix('}') {
return Some(DecayCopyExpr::new(DecayCopyKind::DecayCopyBrace, inner.trim()));
}
}
None
}
}
#[derive(Debug, Clone)]
pub struct WarningDirective {
pub message: String,
pub line: usize,
pub file: String,
}
impl WarningDirective {
pub fn new(message: &str, line: usize, file: &str) -> Self {
Self {
message: message.to_string(),
line,
file: file.to_string(),
}
}
pub fn format_diagnostic(&self) -> String {
format!("{}:{}: warning: {}", self.file, self.line, self.message)
}
}
pub struct WarningDirectiveHandler {
pub warnings: Vec<WarningDirective>,
pub treat_warnings_as_errors: bool,
}
impl WarningDirectiveHandler {
pub fn new() -> Self {
Self {
warnings: Vec::new(),
treat_warnings_as_errors: false,
}
}
pub fn handle(&mut self, message: &str, line: usize, file: &str) {
let warning = WarningDirective::new(message, line, file);
self.warnings.push(warning);
}
pub fn has_warnings(&self) -> bool {
!self.warnings.is_empty()
}
}
impl Default for WarningDirectiveHandler {
fn default() -> Self {
Self::new()
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum ElifConditional {
Elif,
Elifdef,
Elifndef,
}
impl ElifConditional {
pub fn from_directive(s: &str) -> Option<Self> {
match s.trim() {
"elif" => Some(Self::Elif),
"elifdef" => Some(Self::Elifdef),
"elifndef" => Some(Self::Elifndef),
_ => None,
}
}
pub fn as_str(&self) -> &'static str {
match self {
Self::Elif => "#elif",
Self::Elifdef => "#elifdef",
Self::Elifndef => "#elifndef",
}
}
}
#[derive(Debug, Clone)]
pub struct ConditionalBlock {
pub condition: String,
pub conditional_type: ElifConditional,
pub body: String,
pub was_taken: bool,
}
pub struct ConditionalChain {
pub blocks: Vec<ConditionalBlock>,
pub branch_taken: bool,
pub warned_missing_endif: bool,
}
impl ConditionalChain {
pub fn new() -> Self {
Self {
blocks: Vec::new(),
branch_taken: false,
warned_missing_endif: false,
}
}
pub fn handle_ifdef(&mut self, macro_name: &str, is_defined: bool) {
let block = ConditionalBlock {
condition: format!("defined({})", macro_name),
conditional_type: ElifConditional::Elif,
body: String::new(),
was_taken: !self.branch_taken && is_defined,
};
if block.was_taken {
self.branch_taken = true;
}
self.blocks.push(block);
}
pub fn handle_ifndef(&mut self, macro_name: &str, is_defined: bool) {
let block = ConditionalBlock {
condition: format!("!defined({})", macro_name),
conditional_type: ElifConditional::Elif,
body: String::new(),
was_taken: !self.branch_taken && !is_defined,
};
if block.was_taken {
self.branch_taken = true;
}
self.blocks.push(block);
}
pub fn handle_elifdef(&mut self, macro_name: &str, is_defined: bool) {
if self.branch_taken {
return; }
let block = ConditionalBlock {
condition: format!("defined({})", macro_name),
conditional_type: ElifConditional::Elifdef,
body: String::new(),
was_taken: is_defined,
};
if is_defined {
self.branch_taken = true;
}
self.blocks.push(block);
}
pub fn handle_elifndef(&mut self, macro_name: &str, is_defined: bool) {
if self.branch_taken {
return;
}
let block = ConditionalBlock {
condition: format!("!defined({})", macro_name),
conditional_type: ElifConditional::Elifndef,
body: String::new(),
was_taken: !is_defined,
};
if !is_defined {
self.branch_taken = true;
}
self.blocks.push(block);
}
pub fn handle_else(&mut self) {
if self.branch_taken {
return;
}
let block = ConditionalBlock {
condition: "else".to_string(),
conditional_type: ElifConditional::Elif,
body: String::new(),
was_taken: true,
};
self.branch_taken = true;
self.blocks.push(block);
}
pub fn reset(&mut self) {
self.blocks.clear();
self.branch_taken = false;
}
}
impl Default for ConditionalChain {
fn default() -> Self {
Self::new()
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum SizeTLiteralSuffix {
Uz,
Z,
}
impl SizeTLiteralSuffix {
pub fn from_suffix(s: &str) -> Option<Self> {
match s {
"uz" | "UZ" | "Uz" | "uZ" => Some(Self::Uz),
"z" | "Z" => Some(Self::Z),
_ => None,
}
}
pub fn as_str(&self) -> &'static str {
match self {
Self::Uz => "uz",
Self::Z => "z",
}
}
pub fn is_signed(&self) -> bool {
matches!(self, Self::Z)
}
pub fn type_name(&self) -> &'static str {
match self {
Self::Uz => "std::size_t",
Self::Z => "std::ptrdiff_t",
}
}
}
#[derive(Debug, Clone)]
pub struct SizeTLiteral {
pub value: String,
pub suffix: SizeTLiteralSuffix,
}
impl SizeTLiteral {
pub fn new(value: &str, suffix: SizeTLiteralSuffix) -> Self {
Self {
value: value.to_string(),
suffix,
}
}
pub fn to_source(&self) -> String {
format!("{}{}", self.value, self.suffix.as_str())
}
}
pub struct SizeTLiteralParser;
impl SizeTLiteralParser {
pub fn try_parse(input: &str) -> Option<SizeTLiteral> {
let trimmed = input.trim();
for suffix_str in &["uz", "UZ", "Uz", "uZ"] {
if let Some(value) = trimmed.strip_suffix(suffix_str) {
if !value.is_empty()
&& value.chars().all(|c| c.is_ascii_digit() || c == '\'')
{
return Some(SizeTLiteral::new(value, SizeTLiteralSuffix::Uz));
}
}
}
for suffix_str in &["z", "Z"] {
if let Some(value) = trimmed.strip_suffix(suffix_str) {
if !value.is_empty()
&& value.chars().all(|c| c.is_ascii_digit() || c == '\'')
{
return Some(SizeTLiteral::new(value, SizeTLiteralSuffix::Z));
}
}
}
None
}
}
#[derive(Debug, Clone)]
pub struct ConstexprExtensions {
pub constexpr_virtual: bool,
pub constexpr_try_catch: bool,
pub constexpr_dynamic_cast: bool,
pub constexpr_union: bool,
pub constexpr_unique_ptr: bool,
pub constexpr_placement_new: bool,
}
impl Default for ConstexprExtensions {
fn default() -> Self {
Self {
constexpr_virtual: true,
constexpr_try_catch: true,
constexpr_dynamic_cast: true,
constexpr_union: true,
constexpr_unique_ptr: false,
constexpr_placement_new: false,
}
}
}
#[derive(Debug, Clone)]
pub struct ConstexprVirtualFunction {
pub name: String,
pub class_name: String,
pub is_override: bool,
pub is_final: bool,
pub is_pure_virtual: bool,
pub vtable_index: Option<usize>,
}
impl ConstexprVirtualFunction {
pub fn new(name: &str, class_name: &str) -> Self {
Self {
name: name.to_string(),
class_name: class_name.to_string(),
is_override: false,
is_final: false,
is_pure_virtual: false,
vtable_index: None,
}
}
pub fn validate(&self) -> Result<(), String> {
if self.is_pure_virtual {
return Err(format!(
"constexpr virtual function '{}' cannot be pure virtual",
self.name
));
}
Ok(())
}
}
#[derive(Debug, Clone)]
pub struct ConstexprTryCatch {
pub is_constexpr: bool,
pub catch_types: Vec<String>,
pub can_be_constexpr: bool,
}
impl ConstexprTryCatch {
pub fn new() -> Self {
Self {
is_constexpr: false,
catch_types: Vec::new(),
can_be_constexpr: true,
}
}
pub fn validate(&self) -> Result<(), String> {
if !self.is_constexpr {
return Ok(());
}
Ok(())
}
}
impl Default for ConstexprTryCatch {
fn default() -> Self {
Self::new()
}
}
#[derive(Debug, Clone)]
pub struct ConstexprDynamicCast {
pub src_type: String,
pub dst_type: String,
pub is_downcast: bool,
pub is_crosscast: bool,
}
impl ConstexprDynamicCast {
pub fn new(src_type: &str, dst_type: &str) -> Self {
Self {
src_type: src_type.to_string(),
dst_type: dst_type.to_string(),
is_downcast: false,
is_crosscast: false,
}
}
pub fn validate(&self) -> Result<(), String> {
if self.is_crosscast {
}
Ok(())
}
}
#[derive(Debug, Clone)]
pub struct UnreachableBuiltin {
pub name: String,
pub is_noreturn: bool,
pub emit_trap: bool,
}
impl UnreachableBuiltin {
pub fn new() -> Self {
Self {
name: "std::unreachable".to_string(),
is_noreturn: true,
emit_trap: true,
}
}
pub fn emit_llvm(&self) -> String {
if self.emit_trap {
"call void @llvm.trap()\n unreachable".to_string()
} else {
"unreachable".to_string()
}
}
pub fn description(&self) -> &'static str {
"std::unreachable() indicates that a code path cannot be reached. \
If reached at runtime, behavior is undefined. At compile time, \
the optimizer may use this to eliminate dead code."
}
}
impl Default for UnreachableBuiltin {
fn default() -> Self {
Self::new()
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum OptionalMonadicOp {
AndThen,
OrElse,
Transform,
}
impl fmt::Display for OptionalMonadicOp {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::AndThen => write!(f, "and_then"),
Self::OrElse => write!(f, "or_else"),
Self::Transform => write!(f, "transform"),
}
}
}
#[derive(Debug, Clone)]
pub struct OptionalMonadic {
pub value_type: String,
pub monadic_enabled: bool,
}
impl OptionalMonadic {
pub fn new(value_type: &str) -> Self {
Self {
value_type: value_type.to_string(),
monadic_enabled: true,
}
}
pub fn and_then_return_type(&self, f_return_optional_of: &str) -> String {
format!("std::optional<{}>", f_return_optional_of)
}
pub fn transform_return_type(&self, f_return_type: &str) -> String {
format!("std::optional<{}>", f_return_type)
}
pub fn or_else_return_type(&self) -> String {
format!("std::optional<{}>", self.value_type)
}
pub fn codegen_and_then(&self) -> String {
format!(
"if (this->has_value()) {{\n return f(**this); // f returns optional<U>\n}} else {{\n return std::nullopt;\n}}"
)
}
pub fn codegen_or_else(&self) -> String {
format!(
"if (this->has_value()) {{\n return *this;\n}} else {{\n return f();\n}}"
)
}
pub fn codegen_transform(&self) -> String {
format!(
"if (this->has_value()) {{\n return std::optional<U>(f(**this));\n}} else {{\n return std::nullopt;\n}}"
)
}
}
#[derive(Debug, Clone)]
pub enum ExpectedError {
Unexpected(String),
Default,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum ExpectedMonadicOp {
AndThen,
OrElse,
Transform,
TransformError,
}
impl fmt::Display for ExpectedMonadicOp {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::AndThen => write!(f, "and_then"),
Self::OrElse => write!(f, "or_else"),
Self::Transform => write!(f, "transform"),
Self::TransformError => write!(f, "transform_error"),
}
}
}
#[derive(Debug, Clone)]
pub struct ExpectedType {
pub value_type: String,
pub error_type: String,
pub monadic_enabled: bool,
}
impl ExpectedType {
pub fn new(value_type: &str, error_type: &str) -> Self {
Self {
value_type: value_type.to_string(),
error_type: error_type.to_string(),
monadic_enabled: true,
}
}
pub fn validate(&self) -> Result<(), String> {
if self.value_type == "void" {
}
if self.error_type == "void" {
return Err("std::expected error type cannot be void".to_string());
}
Ok(())
}
pub fn and_then_return_type(&self, f_return_expected_of: &str) -> String {
format!(
"std::expected<{}, {}>",
f_return_expected_of, self.error_type
)
}
pub fn transform_return_type(&self, f_return_type: &str) -> String {
format!(
"std::expected<{}, {}>",
f_return_type, self.error_type
)
}
pub fn transform_error_return_type(&self, f_return_error: &str) -> String {
format!(
"std::expected<{}, {}>",
self.value_type, f_return_error
)
}
pub fn or_else_return_type(&self) -> String {
format!("std::expected<{}, {}>", self.value_type, self.error_type)
}
pub fn codegen_and_then(&self) -> String {
"if (has_value) return invoke(f, value); else return unexpected(error);".to_string()
}
pub fn codegen_or_else(&self) -> String {
"if (has_value) return expected(value, in_place); else return invoke(f, error);".to_string()
}
pub fn codegen_transform(&self) -> String {
"if (has_value) return expected(invoke(f, value)); else return unexpected(error);".to_string()
}
pub fn codegen_transform_error(&self) -> String {
"if (!has_value) return expected(unexpected(invoke(f, error))); else return expected(value, in_place);".to_string()
}
}
#[derive(Debug, Clone)]
pub enum ExpectedValue<T, E> {
Ok(T),
Err(E),
}
impl<T, E> ExpectedValue<T, E> {
pub fn is_ok(&self) -> bool {
matches!(self, Self::Ok(_))
}
pub fn is_err(&self) -> bool {
matches!(self, Self::Err(_))
}
pub fn map<U, F: FnOnce(T) -> U>(self, f: F) -> ExpectedValue<U, E> {
match self {
Self::Ok(v) => ExpectedValue::Ok(f(v)),
Self::Err(e) => ExpectedValue::Err(e),
}
}
pub fn and_then<U, F: FnOnce(T) -> ExpectedValue<U, E>>(self, f: F) -> ExpectedValue<U, E> {
match self {
Self::Ok(v) => f(v),
Self::Err(e) => ExpectedValue::Err(e),
}
}
pub fn or_else<F: FnOnce(E) -> ExpectedValue<T, E>>(self, f: F) -> ExpectedValue<T, E> {
match self {
Self::Ok(v) => ExpectedValue::Ok(v),
Self::Err(e) => f(e),
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum RangeViewAdaptor {
Zip,
ZipTransform,
Adjacent,
AdjacentTransform,
CartesianProduct,
Enumerate,
AsConst,
AsRvalue,
Repeat,
Stride,
Slide,
Chunk,
ChunkBy,
JoinWith,
Iota,
}
impl RangeViewAdaptor {
pub fn name(&self) -> &'static str {
match self {
Self::Zip => "zip",
Self::ZipTransform => "zip_transform",
Self::Adjacent => "adjacent",
Self::AdjacentTransform => "adjacent_transform",
Self::CartesianProduct => "cartesian_product",
Self::Enumerate => "enumerate",
Self::AsConst => "as_const",
Self::AsRvalue => "as_rvalue",
Self::Repeat => "repeat",
Self::Stride => "stride",
Self::Slide => "slide",
Self::Chunk => "chunk",
Self::ChunkBy => "chunk_by",
Self::JoinWith => "join_with",
Self::Iota => "iota",
}
}
pub fn header(&self) -> &'static str {
match self {
Self::Zip | Self::ZipTransform | Self::Adjacent | Self::AdjacentTransform => {
"<ranges>"
}
Self::CartesianProduct | Self::Enumerate | Self::AsConst | Self::AsRvalue => {
"<ranges>"
}
Self::Repeat | Self::Stride | Self::Slide | Self::Chunk | Self::ChunkBy
| Self::JoinWith | Self::Iota => "<ranges>",
}
}
}
#[derive(Debug, Clone)]
pub struct RangeViewConfig {
pub adaptor: RangeViewAdaptor,
pub element_type: String,
pub is_const: bool,
pub is_sized: bool,
pub is_borrowed: bool,
pub is_common: bool,
}
impl RangeViewConfig {
pub fn new(adaptor: RangeViewAdaptor, element_type: &str) -> Self {
let (is_sized, is_borrowed, is_common) = match adaptor {
RangeViewAdaptor::Zip => (true, false, true),
RangeViewAdaptor::ZipTransform => (true, false, true),
RangeViewAdaptor::Adjacent => (true, false, false),
RangeViewAdaptor::AdjacentTransform => (true, false, false),
RangeViewAdaptor::CartesianProduct => (true, false, true),
RangeViewAdaptor::Enumerate => (true, false, true),
RangeViewAdaptor::AsConst => (true, false, true),
RangeViewAdaptor::AsRvalue => (true, false, true),
RangeViewAdaptor::Repeat => (false, false, false),
RangeViewAdaptor::Stride => (true, false, false),
RangeViewAdaptor::Slide => (true, false, false),
RangeViewAdaptor::Chunk => (true, false, false),
RangeViewAdaptor::ChunkBy => (false, false, false),
RangeViewAdaptor::JoinWith => (false, false, false),
RangeViewAdaptor::Iota => (true, false, true),
};
Self {
adaptor,
element_type: element_type.to_string(),
is_const: false,
is_sized,
is_borrowed,
is_common,
}
}
}
#[derive(Debug, Clone)]
pub struct ZipIterator {
pub ranges_count: usize,
pub value_types: Vec<String>,
}
impl ZipIterator {
pub fn new(count: usize, types: Vec<String>) -> Self {
Self {
ranges_count: count,
value_types: types,
}
}
pub fn deref_type(&self) -> String {
format!(
"std::tuple<{}>",
self.value_types
.iter()
.map(|t| format!("{}&", t))
.collect::<Vec<_>>()
.join(", ")
)
}
}
#[derive(Debug, Clone)]
pub struct EnumerateIterator {
pub value_type: String,
pub index_type: String,
}
impl EnumerateIterator {
pub fn new(value_type: &str) -> Self {
Self {
value_type: value_type.to_string(),
index_type: "std::size_t".to_string(),
}
}
pub fn element_type(&self) -> String {
format!(
"std::tuple<{}, {}&>",
self.index_type, self.value_type
)
}
}
#[derive(Debug, Clone)]
pub struct ChunkView {
pub chunk_size: usize,
pub element_type: String,
}
impl ChunkView {
pub fn new(chunk_size: usize, element_type: &str) -> Self {
Self {
chunk_size,
element_type: element_type.to_string(),
}
}
pub fn inner_range_type(&self) -> String {
format!("std::ranges::take_view<std::ranges::ref_view<{}>>", self.element_type)
}
}
#[derive(Debug, Clone)]
pub struct CartesianProductView {
pub first_type: String,
pub second_type: String,
}
impl CartesianProductView {
pub fn new(first: &str, second: &str) -> Self {
Self {
first_type: first.to_string(),
second_type: second.to_string(),
}
}
pub fn element_type(&self) -> String {
format!(
"std::tuple<{}&, {}&>",
self.first_type, self.second_type
)
}
}
pub struct RangesAdaptorRegistry {
adaptors: BTreeMap<String, RangeViewConfig>,
}
impl RangesAdaptorRegistry {
pub fn new() -> Self {
let mut registry = Self {
adaptors: BTreeMap::new(),
};
for adaptor in &[
RangeViewAdaptor::Zip,
RangeViewAdaptor::ZipTransform,
RangeViewAdaptor::Adjacent,
RangeViewAdaptor::AdjacentTransform,
RangeViewAdaptor::CartesianProduct,
RangeViewAdaptor::Enumerate,
RangeViewAdaptor::AsConst,
RangeViewAdaptor::AsRvalue,
RangeViewAdaptor::Repeat,
RangeViewAdaptor::Stride,
RangeViewAdaptor::Slide,
RangeViewAdaptor::Chunk,
RangeViewAdaptor::ChunkBy,
RangeViewAdaptor::JoinWith,
RangeViewAdaptor::Iota,
] {
let config = RangeViewConfig::new(*adaptor, "auto");
registry.adaptors.insert(adaptor.name().to_string(), config);
}
registry
}
pub fn lookup(&self, name: &str) -> Option<&RangeViewConfig> {
self.adaptors.get(name)
}
pub fn is_adaptor(&self, name: &str) -> bool {
self.adaptors.contains_key(name)
}
pub fn adaptor_names(&self) -> Vec<&str> {
self.adaptors.keys().map(|s| s.as_str()).collect()
}
pub fn count(&self) -> usize {
self.adaptors.len()
}
}
impl Default for RangesAdaptorRegistry {
fn default() -> Self {
Self::new()
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum CXX23Feature {
DeducingThis,
IfConsteval,
StaticOperatorCall,
MultiDimSubscript,
DecayCopy,
WarningDirective,
ElifdefElifndef,
SizeTLiteral,
ConstexprVirtual,
ConstexprTryCatch,
ConstexprDynamicCast,
Unreachable,
OptionalMonadic,
Expected,
ZipView,
ZipTransformView,
EnumerateView,
AdjacentView,
CartesianProductView,
AsConstView,
AsRvalueView,
RepeatView,
StrideView,
SlideView,
ChunkView,
ChunkByView,
JoinWithView,
}
impl CXX23Feature {
pub fn name(&self) -> &'static str {
match self {
Self::DeducingThis => "deducing_this",
Self::IfConsteval => "if_consteval",
Self::StaticOperatorCall => "static_operator_call",
Self::MultiDimSubscript => "multidimensional_subscript",
Self::DecayCopy => "decay_copy",
Self::WarningDirective => "warning_directive",
Self::ElifdefElifndef => "elifdef_elifndef",
Self::SizeTLiteral => "size_t_literal",
Self::ConstexprVirtual => "constexpr_virtual",
Self::ConstexprTryCatch => "constexpr_try_catch",
Self::ConstexprDynamicCast => "constexpr_dynamic_cast",
Self::Unreachable => "unreachable",
Self::OptionalMonadic => "optional_monadic",
Self::Expected => "expected",
Self::ZipView => "zip_view",
Self::ZipTransformView => "zip_transform_view",
Self::EnumerateView => "enumerate_view",
Self::AdjacentView => "adjacent_view",
Self::CartesianProductView => "cartesian_product_view",
Self::AsConstView => "as_const_view",
Self::AsRvalueView => "as_rvalue_view",
Self::RepeatView => "repeat_view",
Self::StrideView => "stride_view",
Self::SlideView => "slide_view",
Self::ChunkView => "chunk_view",
Self::ChunkByView => "chunk_by_view",
Self::JoinWithView => "join_with_view",
}
}
pub fn paper_number(&self) -> &'static str {
match self {
Self::DeducingThis => "P0847R7",
Self::IfConsteval => "P1938R3",
Self::StaticOperatorCall => "P1169R4",
Self::MultiDimSubscript => "P2128R6",
Self::DecayCopy => "P0849R8",
Self::WarningDirective => "P2437R1",
Self::ElifdefElifndef => "P2334R1",
Self::SizeTLiteral => "P0330R8",
Self::ConstexprVirtual => "P1064R0",
Self::ConstexprTryCatch => "P1002R1",
Self::ConstexprDynamicCast => "P1327R1",
Self::Unreachable => "P0627R6",
Self::OptionalMonadic => "P0798R8",
Self::Expected => "P0323R12",
Self::ZipView => "P2321R2",
Self::ZipTransformView => "P2321R2",
Self::EnumerateView => "P2164R9",
Self::AdjacentView => "P2321R2",
Self::CartesianProductView => "P2374R4",
Self::AsConstView => "P2278R4",
Self::AsRvalueView => "P2446R2",
Self::RepeatView => "P2474R2",
Self::StrideView => "P1899R3",
Self::SlideView => "P2442R1",
Self::ChunkView => "P2442R1",
Self::ChunkByView => "P2443R1",
Self::JoinWithView => "P2441R2",
}
}
}
pub struct CXX23FeatureRegistry {
features: HashMap<CXX23Feature, bool>,
standard_version: CXXStandardVersion,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum CXXStandardVersion {
CXX17,
CXX20,
CXX23,
CXX26,
}
impl CXXStandardVersion {
pub fn as_str(&self) -> &'static str {
match self {
Self::CXX17 => "c++17",
Self::CXX20 => "c++20",
Self::CXX23 => "c++23",
Self::CXX26 => "c++26",
}
}
pub fn from_str(s: &str) -> Option<Self> {
match s {
"c++17" | "c++1z" => Some(Self::CXX17),
"c++20" | "c++2a" => Some(Self::CXX20),
"c++23" | "c++2b" => Some(Self::CXX23),
"c++26" | "c++2c" => Some(Self::CXX26),
_ => None,
}
}
}
impl CXX23FeatureRegistry {
pub fn new(standard: CXXStandardVersion) -> Self {
let mut features = HashMap::new();
for feature in &[
CXX23Feature::DeducingThis,
CXX23Feature::IfConsteval,
CXX23Feature::StaticOperatorCall,
CXX23Feature::MultiDimSubscript,
CXX23Feature::DecayCopy,
CXX23Feature::WarningDirective,
CXX23Feature::ElifdefElifndef,
CXX23Feature::SizeTLiteral,
CXX23Feature::ConstexprVirtual,
CXX23Feature::ConstexprTryCatch,
CXX23Feature::ConstexprDynamicCast,
CXX23Feature::Unreachable,
CXX23Feature::OptionalMonadic,
CXX23Feature::Expected,
CXX23Feature::ZipView,
CXX23Feature::ZipTransformView,
CXX23Feature::EnumerateView,
CXX23Feature::AdjacentView,
CXX23Feature::CartesianProductView,
CXX23Feature::AsConstView,
CXX23Feature::AsRvalueView,
CXX23Feature::RepeatView,
CXX23Feature::StrideView,
CXX23Feature::SlideView,
CXX23Feature::ChunkView,
CXX23Feature::ChunkByView,
CXX23Feature::JoinWithView,
] {
let enabled = match standard {
CXXStandardVersion::CXX17 => false,
CXXStandardVersion::CXX20 => matches!(
feature,
CXX23Feature::ConstexprVirtual
| CXX23Feature::ConstexprTryCatch
| CXX23Feature::ConstexprDynamicCast
),
CXXStandardVersion::CXX23 => true,
CXXStandardVersion::CXX26 => true,
};
features.insert(*feature, enabled);
}
Self {
features,
standard_version: standard,
}
}
pub fn is_enabled(&self, feature: CXX23Feature) -> bool {
self.features.get(&feature).copied().unwrap_or(false)
}
pub fn enable(&mut self, feature: CXX23Feature) {
self.features.insert(feature, true);
}
pub fn disable(&mut self, feature: CXX23Feature) {
self.features.insert(feature, false);
}
pub fn standard(&self) -> CXXStandardVersion {
self.standard_version
}
pub fn enabled_features(&self) -> Vec<CXX23Feature> {
self.features
.iter()
.filter_map(|(f, &enabled)| if enabled { Some(*f) } else { None })
.collect()
}
pub fn feature_count(&self) -> usize {
self.features.len()
}
pub fn enabled_count(&self) -> usize {
self.enabled_features().len()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_explicit_object_kind_from_param_str() {
assert_eq!(
ExplicitObjectKind::from_param_str("this auto&& self"),
Some(ExplicitObjectKind::DeducedRef)
);
assert_eq!(
ExplicitObjectKind::from_param_str("this auto& self"),
Some(ExplicitObjectKind::DeducedLvalueRef)
);
assert_eq!(
ExplicitObjectKind::from_param_str("this auto self"),
Some(ExplicitObjectKind::DeducedByValue)
);
assert_eq!(
ExplicitObjectKind::from_param_str("this const auto& self"),
Some(ExplicitObjectKind::DeducedConstLvalueRef)
);
assert_eq!(ExplicitObjectKind::from_param_str("int x"), None);
}
#[test]
fn test_explicit_object_kind_properties() {
let by_value = ExplicitObjectKind::DeducedByValue;
let ref_kind = ExplicitObjectKind::DeducedRef;
let const_ref = ExplicitObjectKind::DeducedConstLvalueRef;
assert!(!by_value.is_reference());
assert!(ref_kind.is_reference());
assert!(const_ref.is_reference());
assert!(!by_value.is_const());
assert!(const_ref.is_const());
}
#[test]
fn test_explicit_object_function_creation() {
let func = ExplicitObjectFunction::new("f", ExplicitObjectKind::DeducedRef, "self");
assert_eq!(func.name, "f");
assert_eq!(func.param_name, "self");
assert!(func.can_bind_lvalue());
assert!(func.can_bind_rvalue());
}
#[test]
fn test_explicit_object_function_lvalue_only() {
let func = ExplicitObjectFunction::new(
"f",
ExplicitObjectKind::DeducedLvalueRef,
"self",
);
assert!(func.can_bind_lvalue());
assert!(!func.can_bind_rvalue());
}
#[test]
fn test_deducing_this_parser_parse_param() {
let mut parser = DeducingThisParser::new();
assert_eq!(
parser.parse_param("this auto&& self"),
Some(ExplicitObjectKind::DeducedRef)
);
assert_eq!(parser.parse_param("int x"), None);
assert_eq!(parser.parse_param("this is not valid"), None);
}
#[test]
fn test_deducing_this_parser_validate_constructor() {
let mut parser = DeducingThisParser::new();
let mut func = ExplicitObjectFunction::new("Foo", ExplicitObjectKind::DeducedRef, "self");
func.class_name = Some("Foo".to_string());
func.is_member = true;
assert!(!parser.validate(&func));
assert!(!parser.diagnostics.is_empty());
}
#[test]
fn test_deducing_this_parser_deduce_type() {
let parser = DeducingThisParser::new();
let func = ExplicitObjectFunction::new("f", ExplicitObjectKind::DeducedRef, "self");
assert_eq!(parser.deduce_this_type(&func, true), "auto&");
assert_eq!(parser.deduce_this_type(&func, false), "auto&&");
}
#[test]
fn test_consteval_context_enter_leave() {
let mut ctx = ConstevalContext::new();
assert!(!ctx.is_consteval());
ctx.enter(true);
assert!(ctx.is_consteval());
ctx.leave();
assert!(!ctx.is_consteval());
}
#[test]
fn test_consteval_context_nested() {
let mut ctx = ConstevalContext::new();
ctx.enter(false);
assert!(!ctx.is_consteval());
ctx.enter(true);
assert!(ctx.is_consteval());
ctx.leave();
assert!(!ctx.is_consteval());
}
#[test]
fn test_evaluate_if_consteval() {
let mut ctx = ConstevalContext::new();
assert_eq!(
ctx.evaluate_if_consteval(),
IfConstevalResult::NonImmediateContext
);
ctx.enter(true);
assert_eq!(
ctx.evaluate_if_consteval(),
IfConstevalResult::ImmediateContext
);
}
#[test]
fn test_if_consteval_parser_detect() {
let parser = IfConstevalParser::new();
assert!(parser.detect_consteval(&["if", "consteval"]));
assert!(!parser.detect_consteval(&["if", "constexpr"]));
assert!(!parser.detect_consteval(&["if"]));
}
#[test]
fn test_static_operator_call_validation() {
let mut op = StaticOperatorCall::new("MyLambda");
op.is_lambda = true;
op.has_captures = false;
assert!(op.validate().is_ok());
op.has_captures = true;
assert!(op.validate().is_err());
}
#[test]
fn test_call_operator_kind_display() {
assert_eq!(CallOperatorKind::Static.to_string(), "static operator()");
assert_eq!(CallOperatorKind::Const.to_string(), "operator() const");
}
#[test]
fn test_multi_dim_subscript_validation() {
let sub = MultiDimSubscript::new(2);
assert!(sub.validate().is_ok());
let sub0 = MultiDimSubscript::new(0);
assert!(sub0.validate().is_err());
}
#[test]
fn test_multi_dim_subscript_call_syntax() {
let sub = MultiDimSubscript::new(2);
assert_eq!(sub.call_syntax("m", &["i", "j"]), "m[i, j]");
}
#[test]
fn test_multi_dim_subscript_lower() {
let sub = MultiDimSubscript::new(2);
let ir = sub.lower_subscript("ptr", &[1, 2]);
assert!(ir.contains("getelementptr"));
assert!(ir.contains("i64 1"));
assert!(ir.contains("i64 2"));
}
#[test]
fn test_decay_copy_parser_paren() {
let expr = DecayCopyParser::try_parse("auto(x)");
assert!(expr.is_some());
let expr = expr.unwrap();
assert_eq!(expr.kind, DecayCopyKind::DecayCopyParen);
assert_eq!(expr.inner_expr, "x");
}
#[test]
fn test_decay_copy_parser_brace() {
let expr = DecayCopyParser::try_parse("auto{x}");
assert!(expr.is_some());
let expr = expr.unwrap();
assert_eq!(expr.kind, DecayCopyKind::DecayCopyBrace);
assert_eq!(expr.inner_expr, "x");
}
#[test]
fn test_decay_copy_parser_not_decay() {
assert!(DecayCopyParser::try_parse("42").is_none());
assert!(DecayCopyParser::try_parse("auto").is_none());
}
#[test]
fn test_decay_copy_apply_decay_const_ref() {
let expr = DecayCopyExpr::new(DecayCopyKind::DecayCopyParen, "x");
assert_eq!(expr.apply_decay("const int&"), "int");
}
#[test]
fn test_decay_copy_apply_decay_array() {
let expr = DecayCopyExpr::new(DecayCopyKind::DecayCopyParen, "arr");
assert_eq!(expr.apply_decay("int[10]"), "int*");
}
#[test]
fn test_decay_copy_display() {
assert_eq!(
DecayCopyKind::DecayCopyParen.to_string(),
"auto"
);
assert_eq!(
DecayCopyKind::DecayCopyBrace.to_string(),
"auto{}"
);
}
#[test]
fn test_warning_directive_format() {
let w = WarningDirective::new("deprecated API", 42, "test.cpp");
assert!(w.format_diagnostic().contains("test.cpp"));
assert!(w.format_diagnostic().contains("42"));
assert!(w.format_diagnostic().contains("deprecated API"));
}
#[test]
fn test_warning_handler() {
let mut handler = WarningDirectiveHandler::new();
handler.handle("test warning", 10, "file.cpp");
assert!(handler.has_warnings());
assert_eq!(handler.warnings.len(), 1);
}
#[test]
fn test_elif_conditional_from_directive() {
assert_eq!(
ElifConditional::from_directive("elifdef"),
Some(ElifConditional::Elifdef)
);
assert_eq!(
ElifConditional::from_directive("elifndef"),
Some(ElifConditional::Elifndef)
);
assert_eq!(
ElifConditional::from_directive("elif"),
Some(ElifConditional::Elif)
);
assert_eq!(ElifConditional::from_directive("ifndef"), None);
}
#[test]
fn test_conditional_chain_ifdef() {
let mut chain = ConditionalChain::new();
chain.handle_ifdef("FOO", true);
assert!(chain.branch_taken);
assert_eq!(chain.blocks.len(), 1);
}
#[test]
fn test_conditional_chain_elifdef() {
let mut chain = ConditionalChain::new();
chain.handle_ifdef("FOO", false);
assert!(!chain.branch_taken);
chain.handle_elifdef("BAR", true);
assert!(chain.branch_taken);
}
#[test]
fn test_conditional_chain_elifndef() {
let mut chain = ConditionalChain::new();
chain.handle_ifdef("FOO", false);
assert!(!chain.branch_taken);
chain.handle_elifndef("BAR", false);
assert!(chain.branch_taken);
}
#[test]
fn test_conditional_chain_else() {
let mut chain = ConditionalChain::new();
chain.handle_ifdef("FOO", false);
chain.handle_elifdef("BAR", false);
chain.handle_else();
assert!(chain.branch_taken);
}
#[test]
fn test_conditional_chain_reset() {
let mut chain = ConditionalChain::new();
chain.handle_ifdef("FOO", true);
chain.reset();
assert!(!chain.branch_taken);
assert!(chain.blocks.is_empty());
}
#[test]
fn test_size_t_literal_parser_uz() {
let lit = SizeTLiteralParser::try_parse("42uz");
assert!(lit.is_some());
let lit = lit.unwrap();
assert_eq!(lit.value, "42");
assert_eq!(lit.suffix, SizeTLiteralSuffix::Uz);
assert!(!lit.suffix.is_signed());
}
#[test]
fn test_size_t_literal_parser_z() {
let lit = SizeTLiteralParser::try_parse("42z");
assert!(lit.is_some());
let lit = lit.unwrap();
assert_eq!(lit.suffix, SizeTLiteralSuffix::Z);
assert!(lit.suffix.is_signed());
}
#[test]
fn test_size_t_literal_parser_non_literal() {
assert!(SizeTLiteralParser::try_parse("42").is_none());
assert!(SizeTLiteralParser::try_parse("abc").is_none());
}
#[test]
fn test_size_t_literal_to_source() {
let lit = SizeTLiteral::new("100", SizeTLiteralSuffix::Uz);
assert_eq!(lit.to_source(), "100uz");
}
#[test]
fn test_constexpr_extensions_default() {
let ext = ConstexprExtensions::default();
assert!(ext.constexpr_virtual);
assert!(ext.constexpr_try_catch);
assert!(ext.constexpr_dynamic_cast);
}
#[test]
fn test_constexpr_virtual_validate_pure() {
let mut func = ConstexprVirtualFunction::new("f", "Base");
func.is_pure_virtual = true;
assert!(func.validate().is_err());
}
#[test]
fn test_constexpr_virtual_validate_normal() {
let func = ConstexprVirtualFunction::new("f", "Base");
assert!(func.validate().is_ok());
}
#[test]
fn test_constexpr_try_catch_new() {
let tc = ConstexprTryCatch::new();
assert!(!tc.is_constexpr);
assert!(tc.catch_types.is_empty());
assert!(tc.can_be_constexpr);
}
#[test]
fn test_constexpr_dynamic_cast_new() {
let cd = ConstexprDynamicCast::new("Base*", "Derived*");
assert_eq!(cd.src_type, "Base*");
assert_eq!(cd.dst_type, "Derived*");
}
#[test]
fn test_unreachable_emit_llvm() {
let ub = UnreachableBuiltin::new();
let ir = ub.emit_llvm();
assert!(ir.contains("llvm.trap"));
assert!(ir.contains("unreachable"));
}
#[test]
fn test_unreachable_default() {
let ub = UnreachableBuiltin::default();
assert!(ub.is_noreturn);
}
#[test]
fn test_optional_monadic_and_then_return() {
let opt = OptionalMonadic::new("int");
assert_eq!(
opt.and_then_return_type("double"),
"std::optional<double>"
);
}
#[test]
fn test_optional_monadic_transform_return() {
let opt = OptionalMonadic::new("int");
assert_eq!(
opt.transform_return_type("double"),
"std::optional<double>"
);
}
#[test]
fn test_optional_monadic_or_else_return() {
let opt = OptionalMonadic::new("int");
assert_eq!(opt.or_else_return_type(), "std::optional<int>");
}
#[test]
fn test_optional_monadic_codegen_and_then() {
let opt = OptionalMonadic::new("int");
let cg = opt.codegen_and_then();
assert!(cg.contains("has_value"));
assert!(cg.contains("nullopt"));
}
#[test]
fn test_optional_monadic_codegen_or_else() {
let opt = OptionalMonadic::new("int");
let cg = opt.codegen_or_else();
assert!(cg.contains("has_value"));
assert!(cg.contains("f()"));
}
#[test]
fn test_optional_monadic_op_display() {
assert_eq!(OptionalMonadicOp::AndThen.to_string(), "and_then");
assert_eq!(OptionalMonadicOp::OrElse.to_string(), "or_else");
assert_eq!(OptionalMonadicOp::Transform.to_string(), "transform");
}
#[test]
fn test_expected_validation() {
let exp = ExpectedType::new("int", "std::error_code");
assert!(exp.validate().is_ok());
}
#[test]
fn test_expected_validation_error_void() {
let exp = ExpectedType::new("int", "void");
assert!(exp.validate().is_err());
}
#[test]
fn test_expected_and_then_return() {
let exp = ExpectedType::new("int", "std::string");
assert_eq!(
exp.and_then_return_type("double"),
"std::expected<double, std::string>"
);
}
#[test]
fn test_expected_transform_error_return() {
let exp = ExpectedType::new("int", "std::string");
assert_eq!(
exp.transform_error_return_type("int"),
"std::expected<int, int>"
);
}
#[test]
fn test_expected_monadic_op_display() {
assert_eq!(ExpectedMonadicOp::AndThen.to_string(), "and_then");
assert_eq!(ExpectedMonadicOp::TransformError.to_string(), "transform_error");
}
#[test]
fn test_expected_value_is_ok() {
let v: ExpectedValue<i32, String> = ExpectedValue::Ok(42);
assert!(v.is_ok());
assert!(!v.is_err());
}
#[test]
fn test_expected_value_is_err() {
let v: ExpectedValue<i32, String> = ExpectedValue::Err("bad".into());
assert!(!v.is_ok());
assert!(v.is_err());
}
#[test]
fn test_expected_value_map() {
let v: ExpectedValue<i32, String> = ExpectedValue::Ok(2);
let mapped = v.map(|x| x * 3);
assert!(mapped.is_ok());
match mapped {
ExpectedValue::Ok(val) => assert_eq!(val, 6),
_ => panic!("Expected Ok"),
}
}
#[test]
fn test_expected_value_and_then() {
let v: ExpectedValue<i32, String> = ExpectedValue::Ok(1);
let result = v.and_then(|x| ExpectedValue::Ok(x + 1));
assert!(result.is_ok());
}
#[test]
fn test_expected_value_or_else() {
let v: ExpectedValue<i32, String> = ExpectedValue::Err("fail".into());
let result = v.or_else(|_| ExpectedValue::Ok(0));
assert!(result.is_ok());
}
#[test]
fn test_range_view_adaptor_names() {
assert_eq!(RangeViewAdaptor::Zip.name(), "zip");
assert_eq!(RangeViewAdaptor::Enumerate.name(), "enumerate");
assert_eq!(RangeViewAdaptor::Chunk.name(), "chunk");
assert_eq!(RangeViewAdaptor::Iota.name(), "iota");
}
#[test]
fn test_range_view_config_zip() {
let config = RangeViewConfig::new(RangeViewAdaptor::Zip, "int");
assert!(config.is_sized);
assert!(!config.is_borrowed);
assert!(config.is_common);
}
#[test]
fn test_zip_iterator_deref_type() {
let iter = ZipIterator::new(2, vec!["int".into(), "double".into()]);
let deref = iter.deref_type();
assert!(deref.contains("int&"));
assert!(deref.contains("double&"));
assert!(deref.contains("tuple"));
}
#[test]
fn test_enumerate_iterator_element_type() {
let iter = EnumerateIterator::new("int");
assert!(iter.element_type().contains("size_t"));
assert!(iter.element_type().contains("int&"));
}
#[test]
fn test_ranges_adaptor_registry_new() {
let registry = RangesAdaptorRegistry::new();
assert!(registry.count() > 10);
assert!(registry.is_adaptor("zip"));
assert!(registry.is_adaptor("enumerate"));
assert!(registry.is_adaptor("chunk"));
}
#[test]
fn test_ranges_adaptor_registry_lookup() {
let registry = RangesAdaptorRegistry::new();
assert!(registry.lookup("zip").is_some());
assert!(registry.lookup("nonexistent").is_none());
}
#[test]
fn test_feature_registry_cxx23() {
let reg = CXX23FeatureRegistry::new(CXXStandardVersion::CXX23);
assert!(reg.is_enabled(CXX23Feature::DeducingThis));
assert!(reg.is_enabled(CXX23Feature::OptionalMonadic));
assert!(reg.is_enabled(CXX23Feature::Expected));
assert_eq!(reg.standard(), CXXStandardVersion::CXX23);
}
#[test]
fn test_feature_registry_cxx20() {
let reg = CXX23FeatureRegistry::new(CXXStandardVersion::CXX20);
assert!(!reg.is_enabled(CXX23Feature::DeducingThis));
assert!(reg.is_enabled(CXX23Feature::ConstexprVirtual));
assert!(!reg.is_enabled(CXX23Feature::Expected));
}
#[test]
fn test_feature_registry_cxx17() {
let reg = CXX23FeatureRegistry::new(CXXStandardVersion::CXX17);
assert!(!reg.is_enabled(CXX23Feature::DeducingThis));
assert!(!reg.is_enabled(CXX23Feature::ConstexprVirtual));
}
#[test]
fn test_feature_registry_enable_disable() {
let mut reg = CXX23FeatureRegistry::new(CXXStandardVersion::CXX20);
assert!(!reg.is_enabled(CXX23Feature::DeducingThis));
reg.enable(CXX23Feature::DeducingThis);
assert!(reg.is_enabled(CXX23Feature::DeducingThis));
reg.disable(CXX23Feature::DeducingThis);
assert!(!reg.is_enabled(CXX23Feature::DeducingThis));
}
#[test]
fn test_feature_registry_enabled_count() {
let reg = CXX23FeatureRegistry::new(CXXStandardVersion::CXX23);
assert_eq!(reg.enabled_count(), reg.feature_count());
}
#[test]
fn test_cxx_standard_version_from_str() {
assert_eq!(
CXXStandardVersion::from_str("c++23"),
Some(CXXStandardVersion::CXX23)
);
assert_eq!(
CXXStandardVersion::from_str("c++2b"),
Some(CXXStandardVersion::CXX23)
);
assert_eq!(
CXXStandardVersion::from_str("c++26"),
Some(CXXStandardVersion::CXX26)
);
assert_eq!(CXXStandardVersion::from_str("c++14"), None);
}
#[test]
fn test_feature_paper_numbers() {
assert_eq!(CXX23Feature::DeducingThis.paper_number(), "P0847R7");
assert_eq!(CXX23Feature::Expected.paper_number(), "P0323R12");
assert_eq!(CXX23Feature::EnumerateView.paper_number(), "P2164R9");
}
}