use crate::clang::ast::{
AccessSpecifier, BinaryOp, Decl, Expr, FunctionDecl, QualType, Stmt, TranslationUnit, TypeNode,
UnaryOp, VarDecl,
};
use crate::clang::cpp_ast::{
BaseSpecifier, CXXCompoundStmt, CXXDecl, CXXExpr, CXXMemberDecl, CXXMethodDecl, CXXOperator,
CXXParamDecl, CXXRecordKind, CXXStmt, CXXTranslationUnit, CtorInit, EnumConstant,
LambdaCapture, NestedNameSpecifier, RefQualifier, TemplateParamDecl,
};
use crate::clang::CLangStandard;
use std::collections::{HashMap, HashSet};
use std::fmt;
#[derive(Debug, Clone)]
pub enum DeepTemplateParam {
TypeParam {
name: String,
has_default: bool,
default_type: Option<QualType>,
is_parameter_pack: bool,
},
NonTypeParam {
name: String,
ty: QualType,
has_default: bool,
default_value: Option<String>,
is_parameter_pack: bool,
},
TemplateTemplateParam {
name: String,
params: Vec<DeepTemplateParam>,
has_default: bool,
is_parameter_pack: bool,
},
}
impl fmt::Display for DeepTemplateParam {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::TypeParam {
name,
has_default,
default_type: _,
is_parameter_pack,
} => {
if *is_parameter_pack {
write!(f, "typename... {}", name)?;
} else {
write!(f, "typename {}", name)?;
}
if *has_default {
write!(f, " = /* default */")?;
}
Ok(())
}
Self::NonTypeParam {
name,
ty,
has_default,
default_value: _,
is_parameter_pack,
} => {
write!(f, "{} {}", ty, name)?;
if *is_parameter_pack {
write!(f, "...")?;
}
if *has_default {
write!(f, " = /* default */")?;
}
Ok(())
}
Self::TemplateTemplateParam {
name,
params,
has_default,
is_parameter_pack,
} => {
write!(f, "template<")?;
for (i, p) in params.iter().enumerate() {
if i > 0 {
write!(f, ", ")?;
}
write!(f, "{}", p)?;
}
write!(f, "> class {}", name)?;
if *is_parameter_pack {
write!(f, "...")?;
}
if *has_default {
write!(f, " = /* default */")?;
}
Ok(())
}
}
}
}
impl DeepTemplateParam {
pub fn type_param(name: &str, is_pack: bool) -> Self {
Self::TypeParam {
name: name.to_string(),
has_default: false,
default_type: None,
is_parameter_pack: is_pack,
}
}
pub fn type_param_with_default(name: &str, default: QualType, is_pack: bool) -> Self {
Self::TypeParam {
name: name.to_string(),
has_default: true,
default_type: Some(default),
is_parameter_pack: is_pack,
}
}
pub fn non_type_param(name: &str, ty: QualType, is_pack: bool) -> Self {
Self::NonTypeParam {
name: name.to_string(),
ty,
has_default: false,
default_value: None,
is_parameter_pack: is_pack,
}
}
pub fn name(&self) -> &str {
match self {
Self::TypeParam { name, .. } => name,
Self::NonTypeParam { name, .. } => name,
Self::TemplateTemplateParam { name, .. } => name,
}
}
pub fn is_parameter_pack(&self) -> bool {
match self {
Self::TypeParam {
is_parameter_pack, ..
} => *is_parameter_pack,
Self::NonTypeParam {
is_parameter_pack, ..
} => *is_parameter_pack,
Self::TemplateTemplateParam {
is_parameter_pack, ..
} => *is_parameter_pack,
}
}
pub fn has_default(&self) -> bool {
match self {
Self::TypeParam { has_default, .. } => *has_default,
Self::NonTypeParam { has_default, .. } => *has_default,
Self::TemplateTemplateParam { has_default, .. } => *has_default,
}
}
}
#[derive(Debug, Clone)]
pub enum DeepTemplateArg {
Type(QualType),
NonType(String),
Template(String),
PackExpansion(Vec<DeepTemplateArg>),
}
impl PartialEq for DeepTemplateArg {
fn eq(&self, other: &Self) -> bool {
match (self, other) {
(DeepTemplateArg::Type(a), DeepTemplateArg::Type(b)) => {
std::mem::discriminant(a.base.as_ref()) == std::mem::discriminant(b.base.as_ref())
&& a.is_const == b.is_const
&& a.is_volatile == b.is_volatile
}
(DeepTemplateArg::NonType(a), DeepTemplateArg::NonType(b)) => a == b,
(DeepTemplateArg::Template(a), DeepTemplateArg::Template(b)) => a == b,
(DeepTemplateArg::PackExpansion(a), DeepTemplateArg::PackExpansion(b)) => a == b,
_ => false,
}
}
}
impl fmt::Display for DeepTemplateArg {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::Type(ty) => write!(f, "{}", ty),
Self::NonType(val) => write!(f, "{}", val),
Self::Template(name) => write!(f, "{}", name),
Self::PackExpansion(args) => {
write!(f, "(")?;
for (i, a) in args.iter().enumerate() {
if i > 0 {
write!(f, ", ")?;
}
write!(f, "{}", a)?;
}
write!(f, ")")
}
}
}
}
impl DeepTemplateArg {
pub fn type_arg(ty: QualType) -> Self {
Self::Type(ty)
}
pub fn non_type_arg(val: &str) -> Self {
Self::NonType(val.to_string())
}
pub fn template_arg(name: &str) -> Self {
Self::Template(name.to_string())
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum SfinaeState {
Disabled,
Active,
Failure,
}
impl Default for SfinaeState {
fn default() -> Self {
Self::Disabled
}
}
impl SfinaeState {
pub fn is_active(self) -> bool {
matches!(self, Self::Active)
}
pub fn has_failed(self) -> bool {
matches!(self, Self::Failure)
}
}
#[derive(Debug, Clone)]
pub struct InstantiationDepthTracker {
pub current_depth: usize,
pub max_depth: usize,
pub backtrace: Vec<InstantiationRecord>,
pub sfinae_state: SfinaeState,
}
impl InstantiationDepthTracker {
pub fn new(max_depth: usize) -> Self {
Self {
current_depth: 0,
max_depth,
backtrace: Vec::new(),
sfinae_state: SfinaeState::Disabled,
}
}
pub fn with_default_max() -> Self {
Self::new(1024)
}
pub fn enter(&mut self, template_name: &str, args: &[DeepTemplateArg]) -> bool {
if self.current_depth >= self.max_depth {
return false;
}
self.current_depth += 1;
self.backtrace.push(InstantiationRecord {
template_name: template_name.to_string(),
args: args.to_vec(),
depth: self.current_depth,
});
true
}
pub fn leave(&mut self) {
if self.current_depth > 0 {
self.current_depth -= 1;
self.backtrace.pop();
}
}
pub fn depth(&self) -> usize {
self.current_depth
}
pub fn backtrace_string(&self) -> String {
let mut s = String::new();
for rec in &self.backtrace {
s.push_str(&format!(
" in instantiation of '{}' at depth {}\n",
rec.template_name, rec.depth
));
}
s
}
pub fn enter_sfinae(&mut self) {
self.sfinae_state = SfinaeState::Active;
}
pub fn record_sfinae_failure(&mut self) {
self.sfinae_state = SfinaeState::Failure;
}
pub fn leave_sfinae(&mut self) {
self.sfinae_state = SfinaeState::Disabled;
}
pub fn sfinae_active(&self) -> bool {
self.sfinae_state.is_active()
}
}
impl Default for InstantiationDepthTracker {
fn default() -> Self {
Self::with_default_max()
}
}
#[derive(Debug, Clone)]
pub struct InstantiationRecord {
pub template_name: String,
pub args: Vec<DeepTemplateArg>,
pub depth: usize,
}
#[derive(Debug, Clone)]
pub struct DeepDeductionResult {
pub success: bool,
pub deduced_args: HashMap<String, DeepTemplateArg>,
pub failure_reason: Option<String>,
pub substitution_failure: bool,
}
impl DeepDeductionResult {
pub fn success(deduced_args: HashMap<String, DeepTemplateArg>) -> Self {
Self {
success: true,
deduced_args,
failure_reason: None,
substitution_failure: false,
}
}
pub fn failure(reason: &str) -> Self {
Self {
success: false,
deduced_args: HashMap::new(),
failure_reason: Some(reason.to_string()),
substitution_failure: false,
}
}
pub fn sfinae_failure(reason: &str) -> Self {
Self {
success: false,
deduced_args: HashMap::new(),
failure_reason: Some(reason.to_string()),
substitution_failure: true,
}
}
}
#[derive(Debug, Clone)]
pub struct DeepTemplateDeductor {
pub depth_tracker: InstantiationDepthTracker,
pub enable_sfinae: bool,
}
impl DeepTemplateDeductor {
pub fn new(max_depth: usize) -> Self {
Self {
depth_tracker: InstantiationDepthTracker::new(max_depth),
enable_sfinae: true,
}
}
pub fn without_sfinae() -> Self {
Self {
depth_tracker: InstantiationDepthTracker::new(1024),
enable_sfinae: false,
}
}
pub fn deduce_from_call(
&mut self,
params: &[DeepTemplateParam],
fn_params: &[(String, QualType)],
call_args: &[QualType],
explicit_args: &HashMap<String, DeepTemplateArg>,
) -> DeepDeductionResult {
if self.enable_sfinae {
self.depth_tracker.enter_sfinae();
}
let mut deduced = explicit_args.clone();
for (i, (param_name, param_ty)) in fn_params.iter().enumerate() {
if i >= call_args.len() {
continue;
}
let arg_ty = &call_args[i];
if let Err(reason) = self.deduce_from_type_pair(params, param_ty, arg_ty, &mut deduced)
{
if self.enable_sfinae {
self.depth_tracker.record_sfinae_failure();
let result = DeepDeductionResult::sfinae_failure(&reason);
self.depth_tracker.leave_sfinae();
return result;
}
let result = DeepDeductionResult::failure(&reason);
self.depth_tracker.leave_sfinae();
return result;
}
}
for p in params {
if !p.has_default() && !p.is_parameter_pack() && !deduced.contains_key(p.name()) {
let reason = format!("could not deduce template parameter '{}'", p.name());
if self.enable_sfinae {
self.depth_tracker.record_sfinae_failure();
let result = DeepDeductionResult::sfinae_failure(&reason);
self.depth_tracker.leave_sfinae();
return result;
}
let result = DeepDeductionResult::failure(&reason);
self.depth_tracker.leave_sfinae();
return result;
}
}
if self.enable_sfinae {
self.depth_tracker.leave_sfinae();
}
DeepDeductionResult::success(deduced)
}
fn deduce_from_type_pair(
&self,
params: &[DeepTemplateParam],
param_ty: &QualType,
arg_ty: &QualType,
deduced: &mut HashMap<String, DeepTemplateArg>,
) -> Result<(), String> {
if param_ty == arg_ty {
return Ok(());
}
let param_name = self.extract_template_param_name(param_ty);
if let Some(name) = param_name {
let is_type_param = params
.iter()
.any(|p| matches!(p, DeepTemplateParam::TypeParam { name: n, .. } if n == &name));
if is_type_param {
if let Some(existing) = deduced.get(&name) {
if existing != &DeepTemplateArg::Type(arg_ty.clone()) {
return Err(format!(
"conflicting deduction for '{}': {:?} vs {}",
name, existing, arg_ty
));
}
} else {
deduced.insert(name, DeepTemplateArg::Type(arg_ty.clone()));
}
return Ok(());
}
}
Ok(())
}
fn extract_template_param_name(&self, ty: &QualType) -> Option<String> {
let s = format!("{}", ty);
if s.chars().all(|c| c.is_uppercase() || c == '_') && s.len() <= 3 {
return Some(s);
}
None
}
}
impl Default for DeepTemplateDeductor {
fn default() -> Self {
Self::new(1024)
}
}
#[derive(Debug, Clone)]
pub struct TemplateSpecializationMatcher {
pub primary_template_name: String,
pub partial_specializations: Vec<PartialSpecialization>,
pub explicit_specializations: Vec<ExplicitSpecialization>,
}
#[derive(Debug, Clone)]
pub struct PartialSpecialization {
pub params: Vec<DeepTemplateParam>,
pub pattern_args: Vec<DeepTemplateArg>,
pub decl_name: String,
}
#[derive(Debug, Clone)]
pub struct ExplicitSpecialization {
pub template_args: Vec<DeepTemplateArg>,
pub decl_name: String,
}
impl TemplateSpecializationMatcher {
pub fn new(primary_name: &str) -> Self {
Self {
primary_template_name: primary_name.to_string(),
partial_specializations: Vec::new(),
explicit_specializations: Vec::new(),
}
}
pub fn add_partial_specialization(
&mut self,
params: Vec<DeepTemplateParam>,
pattern_args: Vec<DeepTemplateArg>,
decl_name: &str,
) {
self.partial_specializations.push(PartialSpecialization {
params,
pattern_args,
decl_name: decl_name.to_string(),
});
}
pub fn add_explicit_specialization(&mut self, args: Vec<DeepTemplateArg>, decl_name: &str) {
self.explicit_specializations.push(ExplicitSpecialization {
template_args: args,
decl_name: decl_name.to_string(),
});
}
pub fn find_best_match(&self, args: &[DeepTemplateArg]) -> Option<String> {
for spec in &self.explicit_specializations {
if self.args_match_exactly(&spec.template_args, args) {
return Some(spec.decl_name.clone());
}
}
let mut best: Option<&PartialSpecialization> = None;
for ps in &self.partial_specializations {
if self.partial_specialization_matches(ps, args) {
match best {
None => best = Some(ps),
Some(current) => {
if self.is_more_specialized(ps, current) {
best = Some(ps);
}
}
}
}
}
best.map(|ps| ps.decl_name.clone())
}
fn args_match_exactly(&self, spec_args: &[DeepTemplateArg], args: &[DeepTemplateArg]) -> bool {
spec_args.len() == args.len()
&& spec_args.iter().zip(args).all(|(a, b)| match (a, b) {
(DeepTemplateArg::Type(t1), DeepTemplateArg::Type(t2)) => t1 == t2,
(DeepTemplateArg::NonType(v1), DeepTemplateArg::NonType(v2)) => v1 == v2,
(DeepTemplateArg::Template(n1), DeepTemplateArg::Template(n2)) => n1 == n2,
_ => false,
})
}
fn partial_specialization_matches(
&self,
ps: &PartialSpecialization,
args: &[DeepTemplateArg],
) -> bool {
if ps.pattern_args.len() != args.len() {
return false;
}
ps.pattern_args
.iter()
.zip(args)
.all(|(pat, arg)| self.arg_structurally_matches(pat, arg))
}
fn arg_structurally_matches(&self, pattern: &DeepTemplateArg, arg: &DeepTemplateArg) -> bool {
match pattern {
DeepTemplateArg::Type(_) => matches!(arg, DeepTemplateArg::Type(_)),
DeepTemplateArg::NonType(_) => matches!(arg, DeepTemplateArg::NonType(_)),
DeepTemplateArg::Template(_) => matches!(arg, DeepTemplateArg::Template(_)),
DeepTemplateArg::PackExpansion(_) => true, }
}
fn is_more_specialized(&self, a: &PartialSpecialization, b: &PartialSpecialization) -> bool {
let a_types = a
.pattern_args
.iter()
.filter(|arg| matches!(arg, DeepTemplateArg::Type(_)))
.count();
let b_types = b
.pattern_args
.iter()
.filter(|arg| matches!(arg, DeepTemplateArg::Type(_)))
.count();
if a_types != b_types {
return a_types > b_types;
}
a.params.len() < b.params.len()
}
}
#[derive(Debug, Clone)]
pub struct DeepTemplateInstantiator {
pub deductor: DeepTemplateDeductor,
pub matcher: TemplateSpecializationMatcher,
pub instantiation_cache: HashMap<String, String>,
pub depth_tracker: InstantiationDepthTracker,
}
impl DeepTemplateInstantiator {
pub fn new(primary_template: &str) -> Self {
Self {
deductor: DeepTemplateDeductor::default(),
matcher: TemplateSpecializationMatcher::new(primary_template),
instantiation_cache: HashMap::new(),
depth_tracker: InstantiationDepthTracker::with_default_max(),
}
}
pub fn instantiate(
&mut self,
template_name: &str,
args: &[DeepTemplateArg],
) -> Result<String, String> {
let cache_key = Self::make_cache_key(template_name, args);
if let Some(cached) = self.instantiation_cache.get(&cache_key) {
return Ok(cached.clone());
}
if !self.depth_tracker.enter(template_name, args) {
return Err(format!(
"template instantiation depth ({}) exceeds maximum ({})",
self.depth_tracker.depth(),
self.depth_tracker.max_depth,
));
}
let specialized = self.matcher.find_best_match(args);
let result = if let Some(spec_name) = specialized {
Ok(spec_name)
} else {
Ok(format!("{}<{}>", template_name, Self::format_args(args)))
};
self.depth_tracker.leave();
if let Ok(ref name) = result {
self.instantiation_cache.insert(cache_key, name.clone());
}
result
}
fn make_cache_key(template_name: &str, args: &[DeepTemplateArg]) -> String {
format!("{}<{}>", template_name, Self::format_args(args))
}
fn format_args(args: &[DeepTemplateArg]) -> String {
let parts: Vec<String> = args.iter().map(|a| format!("{}", a)).collect();
parts.join(", ")
}
}
impl Default for DeepTemplateInstantiator {
fn default() -> Self {
Self::new("")
}
}
#[derive(Debug, Clone)]
pub struct TemplateConstraint {
pub name: String,
pub params: Vec<DeepTemplateParam>,
pub constraint_expr: String,
}
impl TemplateConstraint {
pub fn new(name: &str, params: Vec<DeepTemplateParam>, constraint: &str) -> Self {
Self {
name: name.to_string(),
params,
constraint_expr: constraint.to_string(),
}
}
pub fn is_satisfied(&self, _args: &HashMap<String, DeepTemplateArg>) -> bool {
true
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
pub enum DeepConversionRank {
ExactMatch = 0,
Promotion = 1,
StandardConversion = 2,
UserDefinedConversion = 3,
Ellipsis = 4,
NotViable = 5,
}
impl fmt::Display for DeepConversionRank {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::ExactMatch => write!(f, "ExactMatch"),
Self::Promotion => write!(f, "Promotion"),
Self::StandardConversion => write!(f, "StandardConversion"),
Self::UserDefinedConversion => write!(f, "UserDefinedConversion"),
Self::Ellipsis => write!(f, "Ellipsis"),
Self::NotViable => write!(f, "NotViable"),
}
}
}
#[derive(Debug, Clone)]
pub struct DeepConversionSequence {
pub rank: DeepConversionRank,
pub steps: Vec<ConversionStep>,
pub is_reference_binding: bool,
pub is_qualification_adjustment: bool,
pub is_derived_to_base: bool,
pub is_user_defined: bool,
pub user_defined_conversion_fn: Option<String>,
pub is_ambiguous: bool,
pub is_bad: bool,
}
impl DeepConversionSequence {
pub fn exact_match() -> Self {
Self {
rank: DeepConversionRank::ExactMatch,
steps: Vec::new(),
is_reference_binding: false,
is_qualification_adjustment: false,
is_derived_to_base: false,
is_user_defined: false,
user_defined_conversion_fn: None,
is_ambiguous: false,
is_bad: false,
}
}
pub fn promotion() -> Self {
Self {
rank: DeepConversionRank::Promotion,
steps: Vec::new(),
is_reference_binding: false,
is_qualification_adjustment: false,
is_derived_to_base: false,
is_user_defined: false,
user_defined_conversion_fn: None,
is_ambiguous: false,
is_bad: false,
}
}
pub fn standard_conversion() -> Self {
Self {
rank: DeepConversionRank::StandardConversion,
steps: Vec::new(),
is_reference_binding: false,
is_qualification_adjustment: false,
is_derived_to_base: false,
is_user_defined: false,
user_defined_conversion_fn: None,
is_ambiguous: false,
is_bad: false,
}
}
pub fn user_defined(fn_name: &str) -> Self {
Self {
rank: DeepConversionRank::UserDefinedConversion,
steps: Vec::new(),
is_reference_binding: false,
is_qualification_adjustment: false,
is_derived_to_base: false,
is_user_defined: true,
user_defined_conversion_fn: Some(fn_name.to_string()),
is_ambiguous: false,
is_bad: false,
}
}
pub fn ellipsis() -> Self {
Self {
rank: DeepConversionRank::Ellipsis,
steps: Vec::new(),
is_reference_binding: false,
is_qualification_adjustment: false,
is_derived_to_base: false,
is_user_defined: false,
user_defined_conversion_fn: None,
is_ambiguous: false,
is_bad: false,
}
}
pub fn not_viable() -> Self {
Self {
rank: DeepConversionRank::NotViable,
steps: Vec::new(),
is_reference_binding: false,
is_qualification_adjustment: false,
is_derived_to_base: false,
is_user_defined: false,
user_defined_conversion_fn: None,
is_ambiguous: false,
is_bad: true,
}
}
pub fn is_viable(&self) -> bool {
self.rank != DeepConversionRank::NotViable
}
}
#[derive(Debug, Clone)]
pub enum ConversionStep {
LvalueToRvalue,
ArrayToPointer,
FunctionToPointer,
QualificationAdjustment {
added_const: bool,
added_volatile: bool,
},
IntegralPromotion,
FloatingPromotion,
IntegralConversion,
FloatingConversion,
FloatingIntegralConversion,
PointerConversion,
PointerToMemberConversion,
BooleanConversion,
DerivedToBase {
base_name: String,
},
UserDefined {
conversion_fn: String,
},
EllipsisConversion,
}
#[derive(Debug, Clone)]
pub struct DeepOverloadCandidate {
pub decl_name: String,
pub is_template: bool,
pub template_params: Vec<DeepTemplateParam>,
pub param_types: Vec<QualType>,
pub is_variadic: bool,
pub conversion_sequences: Vec<DeepConversionSequence>,
pub viability: CandidateViability,
pub is_builtin: bool,
pub overall_rank: DeepConversionRank,
pub has_constraint_violation: bool,
pub constraint_description: Option<String>,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum CandidateViability {
Viable,
ViableButNotBest,
NotViable,
}
impl DeepOverloadCandidate {
pub fn new(name: &str, param_types: Vec<QualType>, is_variadic: bool) -> Self {
let n_params = param_types.len();
Self {
decl_name: name.to_string(),
is_template: false,
template_params: Vec::new(),
param_types,
is_variadic,
conversion_sequences: Vec::new(),
viability: CandidateViability::Viable,
is_builtin: false,
overall_rank: DeepConversionRank::ExactMatch,
has_constraint_violation: false,
constraint_description: None,
}
}
pub fn with_template(mut self, params: Vec<DeepTemplateParam>) -> Self {
self.is_template = true;
self.template_params = params;
self
}
pub fn with_builtin(mut self) -> Self {
self.is_builtin = true;
self
}
}
#[derive(Debug, Clone)]
pub struct DeepOverloadResolver {
pub candidates: Vec<DeepOverloadCandidate>,
pub enable_adl: bool,
pub current_access: AccessSpecifier,
}
impl DeepOverloadResolver {
pub fn new() -> Self {
Self {
candidates: Vec::new(),
enable_adl: true,
current_access: AccessSpecifier::None,
}
}
pub fn add_candidate(&mut self, candidate: DeepOverloadCandidate) {
self.candidates.push(candidate);
}
pub fn resolve_call(&mut self, call_args: &[QualType]) -> Option<&DeepOverloadCandidate> {
let mut viable: Vec<usize> = Vec::new();
for (i, cand) in self.candidates.iter().enumerate() {
if self.is_viable(cand, call_args) {
viable.push(i);
}
}
if viable.is_empty() {
return None;
}
for idx in &viable {
self.compute_conversion_sequences(*idx, call_args);
}
if viable.len() == 1 {
return Some(&self.candidates[viable[0]]);
}
let best_idx = self.select_best_viable(&viable);
best_idx.map(|i| &self.candidates[i])
}
fn is_viable(&self, cand: &DeepOverloadCandidate, call_args: &[QualType]) -> bool {
let min_params = cand.param_types.len();
let max_params = if cand.is_variadic {
usize::MAX
} else {
min_params
};
if call_args.len() < min_params {
return false;
}
if call_args.len() > max_params && !cand.is_variadic {
return false;
}
for (i, param_ty) in cand.param_types.iter().enumerate() {
if i >= call_args.len() {
break; }
let seq = self.compute_conversion(&call_args[i], param_ty);
if !seq.is_viable() {
return false;
}
}
true
}
fn compute_conversion_sequences(&self, idx: usize, call_args: &[QualType]) {
let _ = idx;
let _ = call_args;
}
fn compute_conversion(&self, from: &QualType, to: &QualType) -> DeepConversionSequence {
if from == to {
return DeepConversionSequence::exact_match();
}
if self.is_promotion(from, to) {
return DeepConversionSequence::promotion();
}
if self.is_standard_convertible(from, to) {
return DeepConversionSequence::standard_conversion();
}
DeepConversionSequence::not_viable()
}
fn is_promotion(&self, from: &QualType, to: &QualType) -> bool {
let from_s = format!("{}", from);
let to_s = format!("{}", to);
if (from_s == "short" || from_s == "unsigned short") && to_s == "int" {
return true;
}
if from_s == "float" && to_s == "double" {
return true;
}
if from_s == "bool" && to_s == "int" {
return true;
}
false
}
fn is_standard_convertible(&self, from: &QualType, to: &QualType) -> bool {
let from_s = format!("{}", from);
let to_s = format!("{}", to);
if from_s == "int" && (to_s == "long" || to_s == "double" || to_s == "float") {
return true;
}
if from_s == "long" && to_s == "double" {
return true;
}
if from_s == "unsigned int" && to_s == "int" {
return true;
}
false
}
fn select_best_viable(&self, viable_indices: &[usize]) -> Option<usize> {
if viable_indices.is_empty() {
return None;
}
if viable_indices.len() == 1 {
return Some(viable_indices[0]);
}
let mut best = viable_indices[0];
for &idx in &viable_indices[1..] {
match self.compare_two_candidates(best, idx) {
OrderingResult::FirstBetter => { }
OrderingResult::SecondBetter => {
best = idx;
}
OrderingResult::Ambiguous => {
}
}
}
Some(best)
}
fn compare_two_candidates(&self, a_idx: usize, b_idx: usize) -> OrderingResult {
let a = &self.candidates[a_idx];
let b = &self.candidates[b_idx];
let mut a_better = false;
let mut b_better = false;
let max_params = a.param_types.len().max(b.param_types.len());
for i in 0..max_params {
let rank_a = if i < a.conversion_sequences.len() {
a.conversion_sequences[i].rank
} else {
DeepConversionRank::Ellipsis
};
let rank_b = if i < b.conversion_sequences.len() {
b.conversion_sequences[i].rank
} else {
DeepConversionRank::Ellipsis
};
if rank_a < rank_b {
a_better = true;
}
if rank_b < rank_a {
b_better = true;
}
}
if a_better && !b_better {
return OrderingResult::FirstBetter;
}
if b_better && !a_better {
return OrderingResult::SecondBetter;
}
if !a.is_template && b.is_template {
return OrderingResult::FirstBetter;
}
if a.is_template && !b.is_template {
return OrderingResult::SecondBetter;
}
if a.is_template && b.is_template {
if a.template_params.len() < b.template_params.len() {
return OrderingResult::FirstBetter;
}
if b.template_params.len() < a.template_params.len() {
return OrderingResult::SecondBetter;
}
}
OrderingResult::Ambiguous
}
pub fn set_adl_enabled(&mut self, enabled: bool) {
self.enable_adl = enabled;
}
pub fn get_candidate_count(&self) -> usize {
self.candidates.len()
}
pub fn get_viable_count(&self, call_args: &[QualType]) -> usize {
self.candidates
.iter()
.filter(|c| self.is_viable(c, call_args))
.count()
}
}
impl Default for DeepOverloadResolver {
fn default() -> Self {
Self::new()
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
enum OrderingResult {
FirstBetter,
SecondBetter,
Ambiguous,
}
#[derive(Debug, Clone)]
pub struct TieBreakingRules {
pub prefer_non_template: bool,
pub prefer_more_specialized_template: bool,
pub prefer_fewer_conversions: bool,
pub prefer_narrower_conversion: bool,
pub prefer_non_variadic: bool,
pub prefer_better_constraint: bool,
}
impl Default for TieBreakingRules {
fn default() -> Self {
Self {
prefer_non_template: true,
prefer_more_specialized_template: true,
prefer_fewer_conversions: true,
prefer_narrower_conversion: true,
prefer_non_variadic: true,
prefer_better_constraint: true,
}
}
}
impl TieBreakingRules {
pub fn apply(&self, a: &DeepOverloadCandidate, b: &DeepOverloadCandidate) -> OrderingResult {
if self.prefer_non_template {
if !a.is_template && b.is_template {
return OrderingResult::FirstBetter;
}
if a.is_template && !b.is_template {
return OrderingResult::SecondBetter;
}
}
if self.prefer_more_specialized_template && a.is_template && b.is_template {
if a.template_params.len() < b.template_params.len() {
return OrderingResult::FirstBetter;
}
if b.template_params.len() < a.template_params.len() {
return OrderingResult::SecondBetter;
}
}
if self.prefer_non_variadic {
if !a.is_variadic && b.is_variadic {
return OrderingResult::FirstBetter;
}
if a.is_variadic && !b.is_variadic {
return OrderingResult::SecondBetter;
}
}
if self.prefer_better_constraint {
if !a.has_constraint_violation && b.has_constraint_violation {
return OrderingResult::FirstBetter;
}
if a.has_constraint_violation && !b.has_constraint_violation {
return OrderingResult::SecondBetter;
}
}
if self.prefer_narrower_conversion {
if a.overall_rank < b.overall_rank {
return OrderingResult::FirstBetter;
}
if b.overall_rank < a.overall_rank {
return OrderingResult::SecondBetter;
}
}
OrderingResult::Ambiguous
}
}
#[derive(Debug, Clone)]
pub enum LookupScope {
Global,
Namespace {
name: String,
parent: Box<LookupScope>,
},
ClassScope {
class_name: String,
parent: Box<LookupScope>,
},
FunctionScope {
function_name: String,
parent: Box<LookupScope>,
},
BlockScope {
block_id: usize,
parent: Box<LookupScope>,
},
TemplateScope {
template_name: String,
parent: Box<LookupScope>,
},
}
impl LookupScope {
pub fn global() -> Self {
Self::Global
}
pub fn namespace(name: &str, parent: LookupScope) -> Self {
Self::Namespace {
name: name.to_string(),
parent: Box::new(parent),
}
}
pub fn class(class_name: &str, parent: LookupScope) -> Self {
Self::ClassScope {
class_name: class_name.to_string(),
parent: Box::new(parent),
}
}
pub fn is_global(&self) -> bool {
matches!(self, Self::Global)
}
pub fn parent(&self) -> Option<&LookupScope> {
match self {
Self::Global => None,
Self::Namespace { parent, .. } => Some(parent),
Self::ClassScope { parent, .. } => Some(parent),
Self::FunctionScope { parent, .. } => Some(parent),
Self::BlockScope { parent, .. } => Some(parent),
Self::TemplateScope { parent, .. } => Some(parent),
}
}
}
impl fmt::Display for LookupScope {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::Global => write!(f, "::"),
Self::Namespace { name, parent } => {
if !parent.is_global() {
write!(f, "{}::", parent)?;
}
write!(f, "{}", name)
}
Self::ClassScope { class_name, parent } => {
write!(f, "{}::{}", parent, class_name)
}
Self::FunctionScope {
function_name,
parent,
} => {
write!(f, "{}::{}", parent, function_name)
}
Self::BlockScope { block_id, parent } => {
write!(f, "{}::block#{}", parent, block_id)
}
Self::TemplateScope {
template_name,
parent,
} => {
write!(f, "{}::{}", parent, template_name)
}
}
}
}
#[derive(Debug, Clone)]
pub struct DeepLookupResult {
pub found: bool,
pub declarations: Vec<LookupDeclaration>,
pub is_ambiguous: bool,
pub lookup_path: Vec<String>,
}
#[derive(Debug, Clone)]
pub struct LookupDeclaration {
pub name: String,
pub kind: DeclarationKind,
pub scope: LookupScope,
pub is_accessible: bool,
pub is_using_decl: bool,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum DeclarationKind {
Namespace,
Class,
Function,
Variable,
Typedef,
Enum,
EnumConstant,
Template,
TypeAlias,
UsingDeclaration,
Constructor,
Destructor,
Operator,
}
impl fmt::Display for DeclarationKind {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::Namespace => write!(f, "namespace"),
Self::Class => write!(f, "class"),
Self::Function => write!(f, "function"),
Self::Variable => write!(f, "variable"),
Self::Typedef => write!(f, "typedef"),
Self::Enum => write!(f, "enum"),
Self::EnumConstant => write!(f, "enumerator"),
Self::Template => write!(f, "template"),
Self::TypeAlias => write!(f, "type alias"),
Self::UsingDeclaration => write!(f, "using-declaration"),
Self::Constructor => write!(f, "constructor"),
Self::Destructor => write!(f, "destructor"),
Self::Operator => write!(f, "operator"),
}
}
}
impl DeepLookupResult {
pub fn empty() -> Self {
Self {
found: false,
declarations: Vec::new(),
is_ambiguous: false,
lookup_path: Vec::new(),
}
}
pub fn found(decls: Vec<LookupDeclaration>) -> Self {
Self {
found: true,
declarations: decls,
is_ambiguous: false,
lookup_path: Vec::new(),
}
}
pub fn ambiguous(decls: Vec<LookupDeclaration>) -> Self {
Self {
found: true,
declarations: decls,
is_ambiguous: true,
lookup_path: Vec::new(),
}
}
}
#[derive(Debug, Clone)]
pub struct DeepNameLookup {
pub symbol_table: HashMap<String, Vec<LookupDeclaration>>,
pub namespace_aliases: HashMap<String, String>,
pub using_declarations: HashMap<String, Vec<String>>,
pub using_directives: Vec<String>,
pub adl_enabled: bool,
pub current_scope: LookupScope,
}
impl DeepNameLookup {
pub fn new() -> Self {
Self {
symbol_table: HashMap::new(),
namespace_aliases: HashMap::new(),
using_declarations: HashMap::new(),
using_directives: Vec::new(),
adl_enabled: true,
current_scope: LookupScope::Global,
}
}
pub fn declare(&mut self, scope: &str, decl: LookupDeclaration) {
let key = Self::make_scoped_key(scope, &decl.name);
self.symbol_table.entry(key).or_default().push(decl);
}
pub fn qualified_lookup(&self, scope: &str, name: &str) -> DeepLookupResult {
let key = Self::make_scoped_key(scope, name);
if let Some(decls) = self.symbol_table.get(&key) {
DeepLookupResult::found(decls.clone())
} else {
DeepLookupResult::empty()
}
}
pub fn unqualified_lookup(&self, name: &str) -> DeepLookupResult {
let mut results = Vec::new();
let mut scope = Some(&self.current_scope);
while let Some(s) = scope {
let scope_str = format!("{}", s);
if let Some(using_names) = self.using_declarations.get(&scope_str) {
for uname in using_names {
let key = Self::make_scoped_key(&scope_str, uname);
if let Some(decls) = self.symbol_table.get(&key) {
results.extend(decls.clone());
}
}
}
let key = Self::make_scoped_key(&scope_str, name);
if let Some(decls) = self.symbol_table.get(&key) {
results.extend(decls.clone());
if !results.is_empty() {
break; }
}
scope = s.parent();
}
for directive in &self.using_directives {
let key = Self::make_scoped_key(directive, name);
if let Some(decls) = self.symbol_table.get(&key) {
results.extend(decls.clone());
}
}
if results.is_empty() {
DeepLookupResult::empty()
} else {
DeepLookupResult::found(results)
}
}
pub fn adl_lookup(&self, name: &str, arg_types: &[QualType]) -> DeepLookupResult {
if !self.adl_enabled {
return DeepLookupResult::empty();
}
let mut associated_namespaces = HashSet::new();
let mut associated_classes = HashSet::new();
for arg in arg_types {
self.collect_associated_namespaces(
arg,
&mut associated_namespaces,
&mut associated_classes,
);
}
let mut results = Vec::new();
for ns in &associated_namespaces {
let key = Self::make_scoped_key(ns, name);
if let Some(decls) = self.symbol_table.get(&key) {
results.extend(decls.clone());
}
}
if results.is_empty() {
DeepLookupResult::empty()
} else {
DeepLookupResult::found(results)
}
}
fn collect_associated_namespaces(
&self,
_ty: &QualType,
namespaces: &mut HashSet<String>,
_classes: &mut HashSet<String>,
) {
namespaces.insert("std".to_string());
}
pub fn add_namespace_alias(&mut self, alias: &str, target: &str) {
self.namespace_aliases
.insert(alias.to_string(), target.to_string());
}
pub fn add_using_declaration(&mut self, scope: &str, name: &str) {
self.using_declarations
.entry(scope.to_string())
.or_default()
.push(name.to_string());
}
pub fn add_using_directive(&mut self, namespace: &str) {
self.using_directives.push(namespace.to_string());
}
pub fn set_scope(&mut self, scope: LookupScope) {
self.current_scope = scope;
}
fn make_scoped_key(scope: &str, name: &str) -> String {
if scope == "::" || scope.is_empty() {
name.to_string()
} else {
format!("{}::{}", scope, name)
}
}
pub fn resolve_alias(&self, alias: &str) -> Option<&str> {
self.namespace_aliases.get(alias).map(|s| s.as_str())
}
}
impl Default for DeepNameLookup {
fn default() -> Self {
Self::new()
}
}
#[derive(Debug, Clone)]
pub struct UsingDeclarationTracker {
pub using_decls: HashMap<String, Vec<UsingDeclInfo>>,
}
#[derive(Debug, Clone)]
pub struct UsingDeclInfo {
pub target_name: String,
pub source_scope: String,
pub is_typename: bool,
pub is_constructor_inherited: bool,
}
impl UsingDeclarationTracker {
pub fn new() -> Self {
Self {
using_decls: HashMap::new(),
}
}
pub fn add(&mut self, scope: &str, info: UsingDeclInfo) {
self.using_decls
.entry(scope.to_string())
.or_default()
.push(info);
}
pub fn find_in_scope(&self, scope: &str, name: &str) -> Vec<&UsingDeclInfo> {
self.using_decls
.get(scope)
.map(|vec| vec.iter().filter(|u| u.target_name == name).collect())
.unwrap_or_default()
}
}
impl Default for UsingDeclarationTracker {
fn default() -> Self {
Self::new()
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum MemberAccess {
Public,
Protected,
Private,
None,
}
impl MemberAccess {
pub fn as_str(&self) -> &'static str {
match self {
Self::Public => "public",
Self::Protected => "protected",
Self::Private => "private",
Self::None => "(none)",
}
}
pub fn from_cpp_access_specifier(spec: &AccessSpecifier) -> Self {
match spec {
AccessSpecifier::Public => Self::Public,
AccessSpecifier::Protected => Self::Protected,
AccessSpecifier::Private => Self::Private,
AccessSpecifier::None => Self::None,
}
}
}
impl fmt::Display for MemberAccess {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.as_str())
}
}
#[derive(Debug, Clone)]
pub struct AccessContext {
pub current_class: Option<String>,
pub current_function: Option<String>,
pub is_friend_of: HashSet<String>,
pub is_member_function: bool,
pub is_static_context: bool,
}
impl AccessContext {
pub fn new(class_name: Option<&str>) -> Self {
let mut ctx = Self {
current_class: class_name.map(|s| s.to_string()),
current_function: None,
is_friend_of: HashSet::new(),
is_member_function: false,
is_static_context: false,
};
if let Some(cn) = class_name {
ctx.is_friend_of.insert(cn.to_string());
}
ctx
}
pub fn with_function(mut self, func_name: &str) -> Self {
self.current_function = Some(func_name.to_string());
self.is_member_function = true;
self
}
pub fn add_friend(mut self, friend_class: &str) -> Self {
self.is_friend_of.insert(friend_class.to_string());
self
}
pub fn is_friend_of(&self, class: &str) -> bool {
self.is_friend_of.contains(class)
}
}
#[derive(Debug, Clone)]
pub struct AccessChecker {
pub current_context: AccessContext,
pub friend_declarations: HashMap<String, HashSet<String>>,
}
impl AccessChecker {
pub fn new(context: AccessContext) -> Self {
Self {
current_context: context,
friend_declarations: HashMap::new(),
}
}
pub fn add_friend(&mut self, befriending_class: &str, friend_class: &str) {
self.friend_declarations
.entry(befriending_class.to_string())
.or_default()
.insert(friend_class.to_string());
}
pub fn check_access(
&self,
member_access: MemberAccess,
member_of_class: &str,
member_name: &str,
) -> AccessResult {
match member_access {
MemberAccess::Public => AccessResult::allowed(),
MemberAccess::Private => {
if self.has_same_class_access(member_of_class) {
AccessResult::allowed()
} else if self.is_friend_class(member_of_class) {
AccessResult::allowed()
} else {
AccessResult::denied(format!(
"'{}' is a private member of '{}'",
member_name, member_of_class
))
}
}
MemberAccess::Protected => {
if self.has_same_class_access(member_of_class) {
AccessResult::allowed()
} else if self.is_derived_class_access(member_of_class) {
AccessResult::allowed()
} else if self.is_friend_class(member_of_class) {
AccessResult::allowed()
} else {
AccessResult::denied(format!(
"'{}' is a protected member of '{}'",
member_name, member_of_class
))
}
}
MemberAccess::None => AccessResult::allowed(),
}
}
fn has_same_class_access(&self, class_name: &str) -> bool {
self.current_context
.current_class
.as_ref()
.map(|c| c == class_name)
.unwrap_or(false)
}
fn is_friend_class(&self, class_name: &str) -> bool {
self.current_context.is_friend_of(class_name)
}
fn is_derived_class_access(&self, _class_name: &str) -> bool {
false
}
pub fn check_base_class_access(
&self,
base_access: MemberAccess,
base_class: &str,
derived_class: &str,
) -> AccessResult {
match base_access {
MemberAccess::Public => AccessResult::allowed(),
MemberAccess::Protected => AccessResult::allowed_with_note(
"'{}' is a protected base of '{}' — accessible within hierarchy",
base_class,
derived_class,
),
MemberAccess::Private => AccessResult::allowed_with_note(
"'{}' is a private base of '{}' — accessible within same TU",
base_class,
derived_class,
),
MemberAccess::None => AccessResult::allowed(),
}
}
pub fn default_access_for(kind: &CXXRecordKind) -> MemberAccess {
match kind {
CXXRecordKind::Class => MemberAccess::Private,
CXXRecordKind::Struct => MemberAccess::Public,
CXXRecordKind::Union => MemberAccess::Public,
}
}
}
#[derive(Debug, Clone)]
pub struct AccessResult {
pub allowed: bool,
pub message: String,
pub is_note: bool,
}
impl AccessResult {
pub fn allowed() -> Self {
Self {
allowed: true,
message: String::new(),
is_note: false,
}
}
pub fn denied(reason: String) -> Self {
Self {
allowed: false,
message: reason,
is_note: false,
}
}
pub fn allowed_with_note(_fmt: &str, _a: &str, _b: &str) -> Self {
Self {
allowed: true,
message: String::new(),
is_note: true,
}
}
}
#[derive(Debug, Clone)]
pub struct FriendManager {
pub friends: HashMap<String, Vec<FriendDeclInfo>>,
}
#[derive(Debug, Clone)]
pub struct FriendDeclInfo {
pub friend_name: String,
pub friend_kind: FriendKind,
pub befriending_class: String,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum FriendKind {
Function,
Class,
FunctionTemplate,
ClassTemplate,
}
impl FriendManager {
pub fn new() -> Self {
Self {
friends: HashMap::new(),
}
}
pub fn add_friend_function(&mut self, befriending_class: &str, friend_name: &str) {
self.friends
.entry(befriending_class.to_string())
.or_default()
.push(FriendDeclInfo {
friend_name: friend_name.to_string(),
friend_kind: FriendKind::Function,
befriending_class: befriending_class.to_string(),
});
}
pub fn add_friend_class(&mut self, befriending_class: &str, friend_class: &str) {
self.friends
.entry(befriending_class.to_string())
.or_default()
.push(FriendDeclInfo {
friend_name: friend_class.to_string(),
friend_kind: FriendKind::Class,
befriending_class: befriending_class.to_string(),
});
}
pub fn is_friend(&self, class_name: &str, candidate: &str) -> bool {
self.friends
.get(class_name)
.map(|friends| friends.iter().any(|f| f.friend_name == candidate))
.unwrap_or(false)
}
pub fn get_friends_of(&self, class_name: &str) -> Vec<&FriendDeclInfo> {
self.friends
.get(class_name)
.map(|v| v.iter().collect())
.unwrap_or_default()
}
}
impl Default for FriendManager {
fn default() -> Self {
Self::new()
}
}
#[derive(Debug, Clone)]
pub struct MemberAccessRules {
pub allow_self_access: bool,
pub allow_derived_access_to_protected: bool,
pub allow_friend_access: bool,
pub enforce_access_control: bool,
}
impl Default for MemberAccessRules {
fn default() -> Self {
Self {
allow_self_access: true,
allow_derived_access_to_protected: true,
allow_friend_access: true,
enforce_access_control: true,
}
}
}
impl MemberAccessRules {
pub fn check_field_access(
&self,
field_access: MemberAccess,
field_name: &str,
field_class: &str,
context: &AccessContext,
) -> AccessResult {
if !self.enforce_access_control {
return AccessResult::allowed();
}
match field_access {
MemberAccess::Public => AccessResult::allowed(),
MemberAccess::Protected => {
if self.allow_self_access && context.current_class.as_deref() == Some(field_class) {
AccessResult::allowed()
} else if self.allow_derived_access_to_protected {
AccessResult::allowed()
} else {
AccessResult::denied(format!(
"cannot access protected member '{}' of '{}'",
field_name, field_class
))
}
}
MemberAccess::Private => {
if self.allow_self_access && context.current_class.as_deref() == Some(field_class) {
AccessResult::allowed()
} else if self.allow_friend_access && context.is_friend_of(field_class) {
AccessResult::allowed()
} else {
AccessResult::denied(format!(
"cannot access private member '{}' of '{}'",
field_name, field_class
))
}
}
MemberAccess::None => AccessResult::allowed(),
}
}
pub fn disable_all_checks(mut self) -> Self {
self.enforce_access_control = false;
self
}
}
#[derive(Debug, Clone)]
pub struct DeepLambdaCapture {
pub name: String,
pub capture_kind: DeepCaptureKind,
pub init_expression: Option<String>,
pub is_implicit: bool,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum DeepCaptureKind {
ByValue,
ByReference,
ThisByValue,
StarThis,
InitCapture,
InitCaptureByRef,
}
impl fmt::Display for DeepCaptureKind {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::ByValue => write!(f, "by-value"),
Self::ByReference => write!(f, "by-reference"),
Self::ThisByValue => write!(f, "this"),
Self::StarThis => write!(f, "*this"),
Self::InitCapture => write!(f, "init-capture"),
Self::InitCaptureByRef => write!(f, "init-capture-by-ref"),
}
}
}
impl DeepLambdaCapture {
pub fn by_value(name: &str) -> Self {
Self {
name: name.to_string(),
capture_kind: DeepCaptureKind::ByValue,
init_expression: None,
is_implicit: false,
}
}
pub fn by_reference(name: &str) -> Self {
Self {
name: name.to_string(),
capture_kind: DeepCaptureKind::ByReference,
init_expression: None,
is_implicit: false,
}
}
pub fn this() -> Self {
Self {
name: "this".to_string(),
capture_kind: DeepCaptureKind::ThisByValue,
init_expression: None,
is_implicit: false,
}
}
pub fn init_capture(name: &str, init: &str) -> Self {
Self {
name: name.to_string(),
capture_kind: DeepCaptureKind::InitCapture,
init_expression: Some(init.to_string()),
is_implicit: false,
}
}
pub fn implicit_this() -> Self {
Self {
name: "this".to_string(),
capture_kind: DeepCaptureKind::ThisByValue,
init_expression: None,
is_implicit: true,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum LambdaCaptureDefault {
ByCopy,
ByReference,
None,
}
impl fmt::Display for LambdaCaptureDefault {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::ByCopy => write!(f, "="),
Self::ByReference => write!(f, "&"),
Self::None => write!(f, "(none)"),
}
}
}
#[derive(Debug, Clone)]
pub struct DeepLambdaExpression {
pub capture_default: LambdaCaptureDefault,
pub explicit_captures: Vec<DeepLambdaCapture>,
pub parameters: Vec<(String, QualType)>,
pub return_type: Option<QualType>,
pub body: String,
pub is_mutable: bool,
pub is_constexpr: bool,
pub is_generic: bool,
pub closure_class_name: String,
pub closure_function_name: String,
pub standard: CLangStandard,
}
impl DeepLambdaExpression {
pub fn new(standard: CLangStandard) -> Self {
let uid = Self::next_closure_id();
Self {
capture_default: LambdaCaptureDefault::None,
explicit_captures: Vec::new(),
parameters: Vec::new(),
return_type: None,
body: String::new(),
is_mutable: false,
is_constexpr: false,
is_generic: false,
closure_class_name: format!("__lambda_{}", uid),
closure_function_name: format!("__lambda_{}::operator()", uid),
standard,
}
}
fn next_closure_id() -> u64 {
use std::sync::atomic::{AtomicU64, Ordering};
static COUNTER: AtomicU64 = AtomicU64::new(1);
COUNTER.fetch_add(1, Ordering::Relaxed)
}
pub fn with_capture_default(mut self, default: LambdaCaptureDefault) -> Self {
self.capture_default = default;
self
}
pub fn add_capture(mut self, capture: DeepLambdaCapture) -> Self {
self.explicit_captures.push(capture);
self
}
pub fn add_parameter(mut self, name: &str, ty: QualType) -> Self {
self.parameters.push((name.to_string(), ty));
self
}
pub fn with_return_type(mut self, ty: QualType) -> Self {
self.return_type = Some(ty);
self
}
pub fn with_body(mut self, body: &str) -> Self {
self.body = body.to_string();
self
}
pub fn mutable(mut self) -> Self {
self.is_mutable = true;
self
}
pub fn constexpr(mut self) -> Self {
self.is_constexpr = true;
self
}
pub fn generic(mut self) -> Self {
self.is_generic = true;
self
}
pub fn is_stateless(&self) -> bool {
self.explicit_captures.is_empty() && self.capture_default == LambdaCaptureDefault::None
}
pub fn has_this_capture(&self) -> bool {
self.explicit_captures.iter().any(|c| {
matches!(
c.capture_kind,
DeepCaptureKind::ThisByValue | DeepCaptureKind::StarThis
)
})
}
pub fn is_init_capture_supported(&self) -> bool {
matches!(
self.standard,
CLangStandard::C17 | CLangStandard::C23 | CLangStandard::Gnu17
)
}
}
impl fmt::Display for DeepLambdaExpression {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "[")?;
if self.capture_default != LambdaCaptureDefault::None {
write!(f, "{}", self.capture_default)?;
if !self.explicit_captures.is_empty() {
write!(f, ", ")?;
}
}
for (i, cap) in self.explicit_captures.iter().enumerate() {
if i > 0 {
write!(f, ", ")?;
}
match cap.capture_kind {
DeepCaptureKind::ByReference => write!(f, "&{}", cap.name)?,
DeepCaptureKind::ByValue => write!(f, "{}", cap.name)?,
DeepCaptureKind::ThisByValue => write!(f, "this")?,
DeepCaptureKind::StarThis => write!(f, "*this")?,
DeepCaptureKind::InitCapture => {
if let Some(ref init) = cap.init_expression {
write!(f, "{} = {}", cap.name, init)?;
} else {
write!(f, "{}", cap.name)?;
}
}
DeepCaptureKind::InitCaptureByRef => {
if let Some(ref init) = cap.init_expression {
write!(f, "&{} = {}", cap.name, init)?;
} else {
write!(f, "&{}", cap.name)?;
}
}
}
}
write!(f, "]")?;
write!(f, "(")?;
for (i, (name, ty)) in self.parameters.iter().enumerate() {
if i > 0 {
write!(f, ", ")?;
}
if self.is_generic {
write!(f, "{} {}", ty, name)?;
} else {
write!(f, "{} {}", ty, name)?;
}
}
write!(f, ")")?;
if self.is_mutable {
write!(f, " mutable")?;
}
if self.is_constexpr {
write!(f, " constexpr")?;
}
if let Some(ref ret) = self.return_type {
write!(f, " -> {}", ret)?;
}
write!(f, " {{ {} }}", self.body)
}
}
#[derive(Debug, Clone)]
pub struct ClosureTypeGenerator {
pub class_name: String,
pub capture_members: Vec<ClosureMember>,
pub call_operator_params: Vec<(String, QualType)>,
pub call_operator_return: Option<QualType>,
pub is_mutable: bool,
pub has_this_capture: bool,
pub conversion_operator_target: Option<String>,
}
#[derive(Debug, Clone)]
pub struct ClosureMember {
pub name: String,
pub ty: QualType,
pub is_reference: bool,
pub init_expression: Option<String>,
}
impl ClosureTypeGenerator {
pub fn from_lambda(lambda: &DeepLambdaExpression) -> Self {
let mut members = Vec::new();
for cap in &lambda.explicit_captures {
members.push(ClosureMember {
name: cap.name.clone(),
ty: QualType::auto_ty(),
is_reference: matches!(
cap.capture_kind,
DeepCaptureKind::ByReference | DeepCaptureKind::InitCaptureByRef
),
init_expression: cap.init_expression.clone(),
});
}
Self {
class_name: lambda.closure_class_name.clone(),
capture_members: members,
call_operator_params: lambda.parameters.clone(),
call_operator_return: lambda.return_type.clone(),
is_mutable: lambda.is_mutable,
has_this_capture: lambda.has_this_capture(),
conversion_operator_target: None,
}
}
pub fn add_conversion_operator(&mut self, target: &str) {
self.conversion_operator_target = Some(target.to_string());
}
pub fn can_convert_to_function_pointer(&self) -> bool {
self.capture_members.is_empty() && !self.has_this_capture
}
pub fn generate_class_definition(&self) -> String {
let mut s = format!("class {} {{\n", self.class_name);
for member in &self.capture_members {
let ref_token = if member.is_reference { "&" } else { "" };
s.push_str(&format!(" {}{} {};\n", member.ty, ref_token, member.name));
}
s.push_str("public:\n");
s.push_str(" ");
if !self.is_mutable {
s.push_str("/* implicitly const */ ");
}
s.push_str(&format!(
"auto operator()({})",
self.call_operator_params
.iter()
.map(|(n, t)| format!("{} {}", t, n))
.collect::<Vec<_>>()
.join(", ")
));
if let Some(ref ret) = self.call_operator_return {
s.push_str(&format!(" -> {}", ret));
}
s.push_str(" { /* body */ }\n");
if self.can_convert_to_function_pointer() {
s.push_str(" operator ");
if let Some(ref target) = self.conversion_operator_target {
s.push_str(&format!("{}()", target));
} else {
s.push_str("auto(*)()");
}
s.push_str(" const noexcept { return +[]{}; }\n");
}
s.push('}');
s
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum DeepExceptionSpec {
None,
Noexcept(bool),
ThrowNothing,
Throws(Vec<String>),
ComputedNoexcept(bool),
}
impl fmt::Display for DeepExceptionSpec {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::None => write!(f, "(none)"),
Self::Noexcept(true) => write!(f, "noexcept"),
Self::Noexcept(false) => write!(f, "noexcept(false)"),
Self::ThrowNothing => write!(f, "throw()"),
Self::Throws(types) => {
write!(f, "throw(")?;
for (i, t) in types.iter().enumerate() {
if i > 0 {
write!(f, ", ")?;
}
write!(f, "{}", t)?;
}
write!(f, ")")
}
Self::ComputedNoexcept(v) => write!(f, "noexcept({})", v),
}
}
}
impl DeepExceptionSpec {
pub fn is_noexcept(&self) -> bool {
matches!(
self,
Self::Noexcept(true) | Self::ThrowNothing | Self::ComputedNoexcept(true)
)
}
pub fn can_throw(&self) -> bool {
!self.is_noexcept()
}
}
#[derive(Debug, Clone)]
pub struct DeepTryBlock {
pub try_body: String,
pub catch_handlers: Vec<DeepCatchHandler>,
pub has_cleanup: bool,
pub exception_spec: DeepExceptionSpec,
}
#[derive(Debug, Clone)]
pub struct DeepCatchHandler {
pub caught_type: Option<QualType>,
pub variable_name: Option<String>,
pub is_catch_all: bool,
pub is_by_reference: bool,
pub body: String,
pub this_adjustment: Option<isize>,
}
impl DeepTryBlock {
pub fn new(body: &str) -> Self {
Self {
try_body: body.to_string(),
catch_handlers: Vec::new(),
has_cleanup: false,
exception_spec: DeepExceptionSpec::None,
}
}
pub fn add_catch_handler(mut self, handler: DeepCatchHandler) -> Self {
self.catch_handlers.push(handler);
self
}
pub fn add_catch_all(mut self, body: &str) -> Self {
self.catch_handlers.push(DeepCatchHandler {
caught_type: None,
variable_name: None,
is_catch_all: true,
is_by_reference: false,
body: body.to_string(),
this_adjustment: None,
});
self
}
pub fn with_cleanup(mut self) -> Self {
self.has_cleanup = true;
self
}
pub fn with_noexcept(mut self, noexcept: bool) -> Self {
self.exception_spec = DeepExceptionSpec::Noexcept(noexcept);
self
}
}
#[derive(Debug, Clone)]
pub struct DeepThrowExpression {
pub operand: Option<String>,
pub exception_type: Option<QualType>,
pub has_exception_object: bool,
pub is_rethrow: bool,
}
impl DeepThrowExpression {
pub fn throw_value(expr: &str, ty: QualType) -> Self {
Self {
operand: Some(expr.to_string()),
exception_type: Some(ty),
has_exception_object: true,
is_rethrow: false,
}
}
pub fn rethrow() -> Self {
Self {
operand: None,
exception_type: None,
has_exception_object: false,
is_rethrow: true,
}
}
}
#[derive(Debug, Clone)]
pub struct StackUnwindingState {
pub phase: UnwindingPhase,
pub exception_object: Option<ExceptionObject>,
pub active_cleanup_regions: Vec<CleanupRegion>,
pub is_unwinding: bool,
pub search_result: SearchResult,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum UnwindingPhase {
Search,
Cleanup,
None,
}
#[derive(Debug, Clone)]
pub struct ExceptionObject {
pub type_name: String,
pub type_info: String,
pub is_rtti_complete: bool,
pub has_vtable_ptr: bool,
}
#[derive(Debug, Clone)]
pub struct CleanupRegion {
pub scope_label: String,
pub cleanup_action: CleanupAction,
pub variables_to_destroy: Vec<VariableToDestroy>,
}
#[derive(Debug, Clone)]
pub struct VariableToDestroy {
pub name: String,
pub ty: QualType,
pub has_destructor: bool,
pub destruction_order: usize,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum CleanupAction {
DestroyLocals,
CallDestructors,
ReleaseLock,
DeallocateMemory,
RestoreState,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum SearchResult {
Found,
NotFound,
Searching,
}
impl StackUnwindingState {
pub fn new() -> Self {
Self {
phase: UnwindingPhase::None,
exception_object: None,
active_cleanup_regions: Vec::new(),
is_unwinding: false,
search_result: SearchResult::Searching,
}
}
pub fn begin_search(&mut self, exception: ExceptionObject) {
self.phase = UnwindingPhase::Search;
self.exception_object = Some(exception);
self.is_unwinding = true;
self.search_result = SearchResult::Searching;
}
pub fn begin_cleanup(&mut self) {
self.phase = UnwindingPhase::Cleanup;
}
pub fn add_cleanup_region(&mut self, region: CleanupRegion) {
self.active_cleanup_regions.push(region);
}
pub fn handler_found(&mut self) {
self.search_result = SearchResult::Found;
}
pub fn no_handler_found(&mut self) {
self.search_result = SearchResult::NotFound;
}
pub fn finish_unwinding(&mut self) {
self.is_unwinding = false;
self.phase = UnwindingPhase::None;
self.exception_object = None;
self.active_cleanup_regions.clear();
}
}
impl Default for StackUnwindingState {
fn default() -> Self {
Self::new()
}
}
#[derive(Debug, Clone)]
pub struct ExceptionSafetyAnalyzer {
pub noexcept_evaluations: HashMap<String, bool>,
pub potential_throws: Vec<PotentialThrow>,
}
#[derive(Debug, Clone)]
pub struct PotentialThrow {
pub location: String,
pub exception_type: Option<String>,
pub is_in_noexcept_context: bool,
pub calls_terminate: bool,
}
impl ExceptionSafetyAnalyzer {
pub fn new() -> Self {
Self {
noexcept_evaluations: HashMap::new(),
potential_throws: Vec::new(),
}
}
pub fn register_noexcept_evaluation(&mut self, expr: &str, result: bool) {
self.noexcept_evaluations.insert(expr.to_string(), result);
}
pub fn add_potential_throw(&mut self, throw: PotentialThrow) {
self.potential_throws.push(throw);
}
pub fn has_potential_throw_in_noexcept(&self) -> bool {
self.potential_throws
.iter()
.any(|t| t.is_in_noexcept_context)
}
pub fn will_terminate(&self) -> bool {
self.potential_throws.iter().any(|t| t.calls_terminate)
}
}
impl Default for ExceptionSafetyAnalyzer {
fn default() -> Self {
Self::new()
}
}
#[derive(Debug, Clone)]
pub struct DeepTypeInfo {
pub type_name: String,
pub mangled_name: String,
pub typeinfo_symbol: String,
pub typeinfo_name_symbol: String,
pub is_polymorphic: bool,
pub has_vtable: bool,
pub rtti_kind: DeepRttiKind,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum DeepRttiKind {
Fundamental,
Pointer,
PointerToMember,
Array,
Function,
Enum,
SingleClass,
SingleInheritanceClass,
VirtualMultipleInheritanceClass,
}
impl fmt::Display for DeepRttiKind {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::Fundamental => write!(f, "fundamental"),
Self::Pointer => write!(f, "pointer"),
Self::PointerToMember => write!(f, "pointer-to-member"),
Self::Array => write!(f, "array"),
Self::Function => write!(f, "function"),
Self::Enum => write!(f, "enum"),
Self::SingleClass => write!(f, "single-class"),
Self::SingleInheritanceClass => write!(f, "si-class"),
Self::VirtualMultipleInheritanceClass => write!(f, "vmi-class"),
}
}
}
#[derive(Debug, Clone)]
pub struct DeepTypeidOperator {
pub operand: TypeidOperand,
pub result_typeinfo: Option<DeepTypeInfo>,
}
#[derive(Debug, Clone)]
pub enum TypeidOperand {
Type(QualType),
Expression(String),
}
impl DeepTypeidOperator {
pub fn typeid_of_type(ty: QualType) -> Self {
Self {
operand: TypeidOperand::Type(ty),
result_typeinfo: None,
}
}
pub fn typeid_of_expr(expr: &str) -> Self {
Self {
operand: TypeidOperand::Expression(expr.to_string()),
result_typeinfo: None,
}
}
pub fn requires_runtime_rtti(&self) -> bool {
match &self.operand {
TypeidOperand::Type(ty) => {
let s = format!("{}", ty);
s.contains("virtual") || s.contains("class")
}
TypeidOperand::Expression(_) => {
true
}
}
}
}
#[derive(Debug, Clone)]
pub struct DeepDynamicCast {
pub source_type: QualType,
pub target_type: QualType,
pub operand: String,
pub strategy: DynCastStrategy,
pub is_reference_cast: bool,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum DynCastStrategy {
Identity,
Downcast,
CrossCast,
ToVoid,
}
impl fmt::Display for DynCastStrategy {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::Identity => write!(f, "identity"),
Self::Downcast => write!(f, "downcast"),
Self::CrossCast => write!(f, "cross-cast"),
Self::ToVoid => write!(f, "to-void"),
}
}
}
impl DeepDynamicCast {
pub fn new(source: QualType, target: QualType, operand: &str) -> Self {
let strategy = Self::determine_strategy(&source, &target);
Self {
source_type: source,
target_type: target,
operand: operand.to_string(),
strategy,
is_reference_cast: false,
}
}
fn determine_strategy(source: &QualType, target: &QualType) -> DynCastStrategy {
let src = format!("{}", source);
let tgt = format!("{}", target);
if src == tgt {
DynCastStrategy::Identity
} else if tgt == "void" {
DynCastStrategy::ToVoid
} else if src.contains("Base") && tgt.contains("Derived") {
DynCastStrategy::Downcast
} else {
DynCastStrategy::CrossCast
}
}
pub fn as_reference_cast(mut self) -> Self {
self.is_reference_cast = true;
self
}
pub fn is_downcast(&self) -> bool {
matches!(self.strategy, DynCastStrategy::Downcast)
}
pub fn needs_rtti(&self) -> bool {
!matches!(self.strategy, DynCastStrategy::Identity)
}
}
#[derive(Debug, Clone)]
pub struct DeepVtableRttiDescriptor {
pub class_name: String,
pub rtti_complete_object_locator: String,
pub type_info_symbol: String,
pub base_class_count: usize,
pub vbase_count: usize,
pub offset_to_top: isize,
pub has_virtual_bases: bool,
}
impl DeepVtableRttiDescriptor {
pub fn new(class_name: &str) -> Self {
Self {
class_name: class_name.to_string(),
rtti_complete_object_locator: format!("__RTTI_COMPLETE_OBJECT_LOCATOR_{}", class_name),
type_info_symbol: format!("_ZTI{}", class_name),
base_class_count: 0,
vbase_count: 0,
offset_to_top: 0,
has_virtual_bases: false,
}
}
pub fn with_bases(mut self, base_count: usize) -> Self {
self.base_class_count = base_count;
self
}
pub fn with_virtual_bases(mut self, vbase_count: usize) -> Self {
self.vbase_count = vbase_count;
self.has_virtual_bases = true;
self
}
pub fn emit_ir_declaration(&self) -> String {
let mut s = String::new();
s.push_str(&format!(
"@{} = external constant i8*\n",
self.type_info_symbol
));
s.push_str(&format!(
"@{} = constant [{} x i8] c\"{}\"\n",
self.rtti_complete_object_locator,
self.class_name.len() + 1,
self.class_name
));
s
}
}
#[derive(Debug, Clone)]
pub struct RttiCompleteObjectLocator {
pub signature: u32,
pub offset: i32,
pub cd_offset: i32,
pub type_descriptor_ptr: String,
pub class_descriptor_ptr: String,
pub self_ptr: String,
}
impl RttiCompleteObjectLocator {
pub const SIGNATURE: u32 = 0;
pub fn new(type_info_ptr: &str, class_desc_ptr: &str, self_name: &str) -> Self {
Self {
signature: Self::SIGNATURE,
offset: 0,
cd_offset: 0,
type_descriptor_ptr: type_info_ptr.to_string(),
class_descriptor_ptr: class_desc_ptr.to_string(),
self_ptr: self_name.to_string(),
}
}
pub fn emit_ir(&self) -> String {
format!(
"@{self} = constant {{ i32, i32, i32, ptr, ptr, ptr }} {{\n\
\x20 i32 {sig},\n\
\x20 i32 {off},\n\
\x20 i32 {cd},\n\
\x20 ptr @{td},\n\
\x20 ptr @{cdesc},\n\
\x20 ptr @{self_name}\n\
}}",
self = self.self_ptr,
sig = self.signature,
off = self.offset,
cd = self.cd_offset,
td = self.type_descriptor_ptr,
cdesc = self.class_descriptor_ptr,
self_name = self.self_ptr
)
}
}
#[derive(Debug, Clone)]
pub struct DeepVtableLayout {
pub class_name: String,
pub mangled_name: String,
pub primary_vtable: Vec<DeepVtableEntry>,
pub secondary_vtables: HashMap<String, Vec<DeepVtableEntry>>,
pub construction_vtables: HashMap<String, Vec<DeepVtableEntry>>,
pub vtt_entries: Vec<VttEntry>,
pub has_virtual_bases: bool,
pub is_polymorphic: bool,
}
#[derive(Debug, Clone)]
pub enum DeepVtableEntry {
OffsetToTop(isize),
RttiPtr(String),
VFuncPtr {
mangled_name: String,
this_adjustment: isize,
vcall_offset: isize,
},
VirtualBaseOffset(isize),
CompleteDtor(String),
DeletingDtor(String),
BaseObjectDtor(String),
VcallOffset(isize),
}
impl fmt::Display for DeepVtableEntry {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::OffsetToTop(off) => write!(f, "offset-to-top({})", off),
Self::RttiPtr(sym) => write!(f, "rtti({})", sym),
Self::VFuncPtr {
mangled_name,
this_adjustment,
vcall_offset,
} => {
write!(f, "vfunc({}", mangled_name)?;
if *this_adjustment != 0 {
write!(f, ", this+{}", this_adjustment)?;
}
if *vcall_offset != 0 {
write!(f, ", vcall+{}", vcall_offset)?;
}
write!(f, ")")
}
Self::VirtualBaseOffset(off) => write!(f, "vbase-offset({})", off),
Self::CompleteDtor(name) => write!(f, "complete-dtor({})", name),
Self::DeletingDtor(name) => write!(f, "deleting-dtor({})", name),
Self::BaseObjectDtor(name) => write!(f, "base-dtor({})", name),
Self::VcallOffset(off) => write!(f, "vcall-offset({})", off),
}
}
}
#[derive(Debug, Clone)]
pub struct VttEntry {
pub vtable_name: String,
pub subobject_name: String,
pub offset: isize,
}
impl DeepVtableLayout {
pub fn new(class_name: &str, mangled_name: &str) -> Self {
Self {
class_name: class_name.to_string(),
mangled_name: mangled_name.to_string(),
primary_vtable: Vec::new(),
secondary_vtables: HashMap::new(),
construction_vtables: HashMap::new(),
vtt_entries: Vec::new(),
has_virtual_bases: false,
is_polymorphic: false,
}
}
pub fn add_offset_to_top(&mut self, offset: isize) {
self.primary_vtable
.push(DeepVtableEntry::OffsetToTop(offset));
}
pub fn add_rtti_ptr(&mut self, symbol: &str) {
self.primary_vtable
.push(DeepVtableEntry::RttiPtr(symbol.to_string()));
}
pub fn add_virtual_function(
&mut self,
mangled_name: &str,
this_adjustment: isize,
vcall_offset: isize,
) {
self.primary_vtable.push(DeepVtableEntry::VFuncPtr {
mangled_name: mangled_name.to_string(),
this_adjustment,
vcall_offset,
});
self.is_polymorphic = true;
}
pub fn add_virtual_base_offset(&mut self, offset: isize) {
self.primary_vtable
.push(DeepVtableEntry::VirtualBaseOffset(offset));
self.has_virtual_bases = true;
}
pub fn add_complete_dtor(&mut self, name: &str) {
self.primary_vtable
.push(DeepVtableEntry::CompleteDtor(name.to_string()));
}
pub fn add_deleting_dtor(&mut self, name: &str) {
self.primary_vtable
.push(DeepVtableEntry::DeletingDtor(name.to_string()));
}
pub fn add_base_dtor(&mut self, name: &str) {
self.primary_vtable
.push(DeepVtableEntry::BaseObjectDtor(name.to_string()));
}
pub fn add_secondary_vtable(&mut self, base_name: &str, entries: Vec<DeepVtableEntry>) {
self.secondary_vtables
.insert(base_name.to_string(), entries);
}
pub fn add_construction_vtable(&mut self, context: &str, entries: Vec<DeepVtableEntry>) {
self.construction_vtables
.insert(context.to_string(), entries);
}
pub fn add_vtt_entry(&mut self, vtable_name: &str, subobject: &str, offset: isize) {
self.vtt_entries.push(VttEntry {
vtable_name: vtable_name.to_string(),
subobject_name: subobject.to_string(),
offset,
});
}
pub fn vtable_size_bytes(&self, pointer_size: usize) -> usize {
self.primary_vtable.len() * pointer_size
}
pub fn total_size_bytes(&self, pointer_size: usize) -> usize {
let mut total = self.primary_vtable.len() * pointer_size;
for vt in self.secondary_vtables.values() {
total += vt.len() * pointer_size;
}
total
}
}
impl fmt::Display for DeepVtableLayout {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
writeln!(f, "vtable for {}:", self.class_name)?;
for (i, entry) in self.primary_vtable.iter().enumerate() {
writeln!(f, " [{}] {}", i, entry)?;
}
for (base, secondary) in &self.secondary_vtables {
writeln!(f, "secondary vtable (in {}):", base)?;
for (i, entry) in secondary.iter().enumerate() {
writeln!(f, " [{}] {}", i, entry)?;
}
}
Ok(())
}
}
#[derive(Debug, Clone)]
pub struct BaseClassLayout {
pub base_name: String,
pub is_virtual: bool,
pub is_primary: bool,
pub offset_bytes: usize,
pub vtable_index: Option<usize>,
}
#[derive(Debug, Clone)]
pub struct DeepClassLayout {
pub class_name: String,
pub size_bytes: usize,
pub alignment_bytes: usize,
pub bases: Vec<BaseClassLayout>,
pub members: Vec<DeepMemberLayout>,
pub vtable_layout: Option<DeepVtableLayout>,
pub has_vtable_ptr: bool,
pub is_empty: bool,
pub needs_tail_padding: bool,
}
#[derive(Debug, Clone)]
pub struct DeepMemberLayout {
pub name: String,
pub ty_name: String,
pub offset_bytes: usize,
pub size_bytes: usize,
pub alignment_bytes: usize,
pub access: MemberAccess,
pub is_static: bool,
}
impl DeepClassLayout {
pub fn new(class_name: &str) -> Self {
Self {
class_name: class_name.to_string(),
size_bytes: 0,
alignment_bytes: 1,
bases: Vec::new(),
members: Vec::new(),
vtable_layout: None,
has_vtable_ptr: false,
is_empty: true,
needs_tail_padding: false,
}
}
pub fn add_base(&mut self, base: BaseClassLayout) {
if base.is_virtual {
self.has_vtable_ptr = true;
}
self.is_empty = false;
self.bases.push(base);
}
pub fn add_member(&mut self, member: DeepMemberLayout) {
self.alignment_bytes = self.alignment_bytes.max(member.alignment_bytes);
let aligned_offset = (self.size_bytes + member.alignment_bytes - 1)
/ member.alignment_bytes
* member.alignment_bytes;
let member_with_padding = DeepMemberLayout {
offset_bytes: aligned_offset,
..member
};
self.size_bytes = aligned_offset + member_with_padding.size_bytes;
self.members.push(member_with_padding);
self.is_empty = false;
}
pub fn set_vtable_layout(&mut self, layout: DeepVtableLayout) {
self.has_vtable_ptr = true;
self.vtable_layout = Some(layout);
self.is_empty = false;
}
pub fn base_offset(&self, base_name: &str) -> Option<usize> {
self.bases
.iter()
.find(|b| b.base_name == base_name)
.map(|b| b.offset_bytes)
}
pub fn member_offset(&self, member_name: &str) -> Option<usize> {
self.members
.iter()
.find(|m| m.name == member_name)
.map(|m| m.offset_bytes)
}
pub fn finalize(&mut self) {
if self.size_bytes > 0 {
self.size_bytes = (self.size_bytes + self.alignment_bytes - 1) / self.alignment_bytes
* self.alignment_bytes;
}
}
}
impl fmt::Display for DeepClassLayout {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
writeln!(
f,
"class {} (size={}, align={}):",
self.class_name, self.size_bytes, self.alignment_bytes
)?;
for base in &self.bases {
let virt = if base.is_virtual { " virtual" } else { "" };
writeln!(
f,
" base {} at +{} ({})",
base.base_name,
base.offset_bytes,
virt.trim()
)?;
}
for member in &self.members {
writeln!(
f,
" {} {} at +{} ({} bytes)",
member.ty_name, member.name, member.offset_bytes, member.size_bytes
)?;
}
if self.has_vtable_ptr {
writeln!(f, " [vtable_ptr at +0]")?;
}
Ok(())
}
}
#[derive(Debug, Clone)]
pub struct ThisAdjustmentThunk {
pub thunk_name: String,
pub target_function: String,
pub this_adjustment_bytes: isize,
pub vcall_offset: isize,
pub return_type: Option<QualType>,
pub param_types: Vec<QualType>,
}
impl ThisAdjustmentThunk {
pub fn new(target: &str, adjustment: isize) -> Self {
Self {
thunk_name: format!("__thunk_{}", target),
target_function: target.to_string(),
this_adjustment_bytes: adjustment,
vcall_offset: 0,
return_type: None,
param_types: Vec::new(),
}
}
pub fn with_vcall(mut self, vcall_offset: isize) -> Self {
self.vcall_offset = vcall_offset;
self.thunk_name = format!("__vcall_thunk_{}", self.target_function);
self
}
pub fn generate_ir(&self) -> String {
let mut s = String::new();
s.push_str(&format!("define void @{}() {{\n", self.thunk_name));
s.push_str("entry:\n");
s.push_str(&format!(
" %adjusted_this = getelementptr i8, ptr %this, i64 {}\n",
self.this_adjustment_bytes
));
if self.vcall_offset != 0 {
s.push_str(&format!(" %vtable = load ptr, ptr %this\n",));
s.push_str(&format!(
" %vcall_ptr = getelementptr ptr, ptr %vtable, i64 {}\n",
self.vcall_offset
));
}
s.push_str(&format!(" tail call void @{}()\n", self.target_function));
s.push_str(" ret void\n");
s.push_str("}\n");
s
}
pub fn is_identity(&self) -> bool {
self.this_adjustment_bytes == 0 && self.vcall_offset == 0
}
}
#[derive(Debug, Clone)]
pub struct PrimaryVtableBuilder {
pub class_name: String,
pub primary_base: Option<String>,
pub virtual_functions: Vec<VirtualFunctionInfo>,
pub introduces_new_vfuncs: bool,
}
#[derive(Debug, Clone)]
pub struct VirtualFunctionInfo {
pub mangled_name: String,
pub return_type: String,
pub param_types: Vec<String>,
pub is_overrider: bool,
pub overridden_function: Option<String>,
pub this_adjustment: isize,
pub is_pure: bool,
}
impl PrimaryVtableBuilder {
pub fn new(class_name: &str) -> Self {
Self {
class_name: class_name.to_string(),
primary_base: None,
virtual_functions: Vec::new(),
introduces_new_vfuncs: false,
}
}
pub fn with_primary_base(mut self, base: &str) -> Self {
self.primary_base = Some(base.to_string());
self
}
pub fn add_virtual_function(mut self, vfunc: VirtualFunctionInfo) -> Self {
if !vfunc.is_overrider {
self.introduces_new_vfuncs = true;
}
self.virtual_functions.push(vfunc);
self
}
pub fn build(&self) -> DeepVtableLayout {
let mangled = format!("_ZTV{}", self.class_name);
let mut layout = DeepVtableLayout::new(&self.class_name, &mangled);
layout.add_offset_to_top(0);
layout.add_rtti_ptr(&format!("_ZTI{}", self.class_name));
for vfunc in &self.virtual_functions {
layout.add_virtual_function(&vfunc.mangled_name, vfunc.this_adjustment, 0);
}
layout.add_complete_dtor(&format!("_ZN{}D1Ev", self.class_name));
layout.add_deleting_dtor(&format!("_ZN{}D0Ev", self.class_name));
layout
}
}
#[derive(Debug, Clone)]
pub struct ItaniumMangler {
buffer: String,
substitutions: HashMap<String, usize>,
template_depth: usize,
next_substitution_index: usize,
}
impl ItaniumMangler {
pub fn new() -> Self {
Self {
buffer: String::new(),
substitutions: HashMap::new(),
template_depth: 0,
next_substitution_index: 0,
}
}
pub fn mangle_function(
&mut self,
namespace: Option<&str>,
name: &str,
param_types: &[String],
) -> String {
self.buffer.clear();
self.buffer.push_str("_Z");
if let Some(ns) = namespace {
self.encode_nested_name(ns, name);
} else {
self.encode_name(name);
}
if param_types.is_empty() {
self.buffer.push('v'); } else {
for ty in param_types {
self.mangle_builtin_type(ty);
}
}
self.buffer.clone()
}
pub fn mangle_member_function(
&mut self,
class_name: &str,
method_name: &str,
param_types: &[String],
is_const: bool,
ref_qual: Option<RefQualifier>,
) -> String {
self.buffer.clear();
self.buffer.push_str("_Z");
self.encode_nested_name(class_name, method_name);
if is_const {
self.buffer.push_str("K");
}
match ref_qual {
Some(RefQualifier::LValue) => self.buffer.push_str("R"),
Some(RefQualifier::RValue) => self.buffer.push_str("O"),
None => {}
}
if param_types.is_empty() {
self.buffer.push('v');
} else {
for ty in param_types {
self.mangle_builtin_type(ty);
}
}
self.buffer.clone()
}
pub fn mangle_constructor(&mut self, class_name: &str, param_types: &[String]) -> String {
self.buffer.clear();
self.buffer.push_str("_Z");
self.encode_nested_name(class_name, "C1");
if param_types.is_empty() {
self.buffer.push('v');
} else {
for ty in param_types {
self.mangle_builtin_type(ty);
}
}
self.buffer.clone()
}
pub fn mangle_destructor(&mut self, class_name: &str) -> String {
self.buffer.clear();
self.buffer.push_str("_Z");
self.encode_nested_name(class_name, "D1");
self.buffer.push('v');
self.buffer.clone()
}
pub fn mangle_vtable(&mut self, class_name: &str) -> String {
format!("_ZTV{}", self.encode_simple_name(class_name))
}
pub fn mangle_typeinfo(&mut self, class_name: &str) -> String {
format!("_ZTI{}", self.encode_simple_name(class_name))
}
pub fn mangle_typeinfo_name(&mut self, class_name: &str) -> String {
format!("_ZTS{}", self.encode_simple_name(class_name))
}
pub fn mangle_guard_variable(&mut self, var_name: &str) -> String {
format!("_ZGV{}", self.encode_simple_name(var_name))
}
pub fn mangle_thunk(&mut self, target: &str, this_adjustment: isize) -> String {
if this_adjustment == 0 {
format!("_ZThn0_{}", target)
} else {
format!("_ZThn{}_{}", this_adjustment, target)
}
}
pub fn mangle_virtual_thunk(&mut self, target: &str, vcall_offset: isize) -> String {
format!("_ZTv{}_n{}", vcall_offset, target)
}
fn encode_nested_name(&mut self, scope: &str, name: &str) {
self.buffer.push('N');
self.buffer.push_str(&format!("{}", scope.len()));
self.buffer.push_str(scope);
self.buffer.push_str(&format!("{}", name.len()));
self.buffer.push_str(name);
self.buffer.push('E');
}
fn encode_name(&mut self, name: &str) {
self.buffer.push_str(&format!("{}", name.len()));
self.buffer.push_str(name);
}
fn encode_simple_name(&self, name: &str) -> String {
format!("{}{}", name.len(), name)
}
fn mangle_builtin_type(&mut self, ty: &str) {
match ty {
"void" => self.buffer.push('v'),
"bool" => self.buffer.push('b'),
"char" => self.buffer.push('c'),
"signed char" | "char" => self.buffer.push('a'),
"unsigned char" => self.buffer.push('h'),
"short" => self.buffer.push('s'),
"unsigned short" => self.buffer.push('t'),
"int" => self.buffer.push('i'),
"unsigned int" => self.buffer.push('j'),
"long" => self.buffer.push('l'),
"unsigned long" => self.buffer.push('m'),
"long long" => self.buffer.push('x'),
"unsigned long long" => self.buffer.push('y'),
"float" => self.buffer.push('f'),
"double" => self.buffer.push('d'),
"long double" => self.buffer.push('e'),
"wchar_t" => self.buffer.push('w'),
"char8_t" => self.buffer.push_str("Du"),
"char16_t" => self.buffer.push_str("Ds"),
"char32_t" => self.buffer.push_str("Di"),
"..." => self.buffer.push('z'),
_ => {
self.buffer.push_str(&format!("{}", ty.len()));
self.buffer.push_str(ty);
}
}
}
pub fn try_substitute(&mut self, key: &str) -> bool {
if let Some(&idx) = self.substitutions.get(key) {
self.buffer.push('S');
if idx > 0 {
self.buffer.push_str(&Self::base36_encode(idx - 1));
}
self.buffer.push('_');
return true;
}
false
}
pub fn add_substitution(&mut self, key: &str) {
if !self.substitutions.contains_key(key) {
self.substitutions
.insert(key.to_string(), self.next_substitution_index);
self.next_substitution_index += 1;
}
}
fn base36_encode(n: usize) -> String {
if n == 0 {
return "0".to_string();
}
let chars: Vec<char> = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ".chars().collect();
let mut result = String::new();
let mut v = n;
while v > 0 {
result.push(chars[v % 36]);
v /= 36;
}
result.chars().rev().collect()
}
}
impl Default for ItaniumMangler {
fn default() -> Self {
Self::new()
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum CallingConvention {
CDecl,
ThisCall,
FastCall,
VectorCall,
SysVAmd64,
MicrosoftX64,
Aapcs,
AapcsVfp,
}
impl fmt::Display for CallingConvention {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::CDecl => write!(f, "cdecl"),
Self::ThisCall => write!(f, "thiscall"),
Self::FastCall => write!(f, "fastcall"),
Self::VectorCall => write!(f, "vectorcall"),
Self::SysVAmd64 => write!(f, "sysv_amd64"),
Self::MicrosoftX64 => write!(f, "ms_x64"),
Self::Aapcs => write!(f, "aapcs"),
Self::AapcsVfp => write!(f, "aapcs_vfp"),
}
}
}
#[derive(Debug, Clone)]
pub struct ThisPointerHandling {
pub has_this_pointer: bool,
pub this_is_pointer: bool,
pub this_is_const: bool,
pub this_is_volatile: bool,
pub this_is_rvalue_ref: bool,
pub this_adjustment_bytes: isize,
pub calling_convention: CallingConvention,
}
impl Default for ThisPointerHandling {
fn default() -> Self {
Self {
has_this_pointer: false,
this_is_pointer: true,
this_is_const: false,
this_is_volatile: false,
this_is_rvalue_ref: false,
this_adjustment_bytes: 0,
calling_convention: CallingConvention::ThisCall,
}
}
}
impl ThisPointerHandling {
pub fn for_const_member_function() -> Self {
Self {
this_is_const: true,
has_this_pointer: true,
..Default::default()
}
}
pub fn for_static_function() -> Self {
Self {
has_this_pointer: false,
..Default::default()
}
}
pub fn with_adjustment(mut self, bytes: isize) -> Self {
self.this_adjustment_bytes = bytes;
self
}
pub fn with_calling_convention(mut self, cc: CallingConvention) -> Self {
self.calling_convention = cc;
self
}
pub fn needs_thunk(&self) -> bool {
self.this_adjustment_bytes != 0
}
}
#[derive(Debug, Clone)]
pub struct MemberFunctionPointer {
pub function_pointer: Option<String>,
pub this_adjustment: isize,
pub is_virtual: bool,
pub vtable_index: Option<usize>,
}
impl MemberFunctionPointer {
pub fn non_virtual(func_ptr: &str, adjustment: isize) -> Self {
Self {
function_pointer: Some(func_ptr.to_string()),
this_adjustment: adjustment,
is_virtual: false,
vtable_index: None,
}
}
pub fn virtual_call(vtable_index: usize, adjustment: isize) -> Self {
Self {
function_pointer: None,
this_adjustment: adjustment,
is_virtual: true,
vtable_index: Some(vtable_index),
}
}
pub fn null() -> Self {
Self {
function_pointer: None,
this_adjustment: 0,
is_virtual: false,
vtable_index: None,
}
}
pub fn is_null(&self) -> bool {
self.function_pointer.is_none() && self.vtable_index.is_none() && self.this_adjustment == 0
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum StructReturnConvention {
InRegisters,
SRet,
ByReference,
}
#[derive(Debug, Clone)]
pub struct AbiInfo {
pub target_triple: String,
pub pointer_size: usize,
pub pointer_alignment: usize,
pub size_t_type: String,
pub ptrdiff_t_type: String,
pub is_itanium_abi: bool,
pub is_microsoft_abi: bool,
pub struct_return_convention: StructReturnConvention,
}
impl AbiInfo {
pub fn for_x86_64_linux() -> Self {
Self {
target_triple: "x86_64-unknown-linux-gnu".to_string(),
pointer_size: 8,
pointer_alignment: 8,
size_t_type: "unsigned long".to_string(),
ptrdiff_t_type: "long".to_string(),
is_itanium_abi: true,
is_microsoft_abi: false,
struct_return_convention: StructReturnConvention::InRegisters,
}
}
pub fn for_aarch64_linux() -> Self {
Self {
target_triple: "aarch64-unknown-linux-gnu".to_string(),
pointer_size: 8,
pointer_alignment: 8,
size_t_type: "unsigned long".to_string(),
ptrdiff_t_type: "long".to_string(),
is_itanium_abi: true,
is_microsoft_abi: false,
struct_return_convention: StructReturnConvention::InRegisters,
}
}
pub fn for_x86_64_windows() -> Self {
Self {
target_triple: "x86_64-pc-windows-msvc".to_string(),
pointer_size: 8,
pointer_alignment: 8,
size_t_type: "unsigned long long".to_string(),
ptrdiff_t_type: "long long".to_string(),
is_itanium_abi: false,
is_microsoft_abi: true,
struct_return_convention: StructReturnConvention::InRegisters,
}
}
}
#[derive(Debug, Clone)]
pub struct VariadicExpansion {
pub pack_name: String,
pub pattern: String,
pub expanded_args: Vec<DeepTemplateArg>,
pub expansion_locus: VariadicLocus,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum VariadicLocus {
TemplateArgumentList,
FunctionParameterList,
BaseSpecifierList,
MemberInitializerList,
LambdaCaptureList,
UsingDeclaration,
}
impl fmt::Display for VariadicLocus {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::TemplateArgumentList => write!(f, "template-argument-list"),
Self::FunctionParameterList => write!(f, "function-parameter-list"),
Self::BaseSpecifierList => write!(f, "base-specifier-list"),
Self::MemberInitializerList => write!(f, "member-initializer-list"),
Self::LambdaCaptureList => write!(f, "lambda-capture-list"),
Self::UsingDeclaration => write!(f, "using-declaration"),
}
}
}
impl VariadicExpansion {
pub fn new(pack: &str, pattern: &str, locus: VariadicLocus) -> Self {
Self {
pack_name: pack.to_string(),
pattern: pattern.to_string(),
expanded_args: Vec::new(),
expansion_locus: locus,
}
}
pub fn expand(&mut self, args: Vec<DeepTemplateArg>) {
self.expanded_args = args;
}
pub fn arg_count(&self) -> usize {
self.expanded_args.len()
}
pub fn is_empty_expansion(&self) -> bool {
self.expanded_args.is_empty()
}
}
#[derive(Debug, Clone)]
pub struct FoldExpressionEvaluator {
pub operator: FoldOperator,
pub is_left_fold: bool,
pub pack: Vec<String>,
pub init: Option<String>,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum FoldOperator {
Add,
Sub,
Mul,
Div,
Mod,
BitAnd,
BitOr,
BitXor,
Shl,
Shr,
Assign,
AddAssign,
SubAssign,
MulAssign,
DivAssign,
ModAssign,
AndAssign,
OrAssign,
XorAssign,
ShlAssign,
ShrAssign,
Eq,
Ne,
Lt,
Gt,
Le,
Ge,
LogicalAnd,
LogicalOr,
Comma,
}
impl fmt::Display for FoldOperator {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::Add => write!(f, "+"),
Self::Sub => write!(f, "-"),
Self::Mul => write!(f, "*"),
Self::Div => write!(f, "/"),
Self::Mod => write!(f, "%"),
Self::BitAnd => write!(f, "&"),
Self::BitOr => write!(f, "|"),
Self::BitXor => write!(f, "^"),
Self::Shl => write!(f, "<<"),
Self::Shr => write!(f, ">>"),
Self::Assign => write!(f, "="),
Self::AddAssign => write!(f, "+="),
Self::SubAssign => write!(f, "-="),
Self::MulAssign => write!(f, "*="),
Self::DivAssign => write!(f, "/="),
Self::ModAssign => write!(f, "%="),
Self::AndAssign => write!(f, "&="),
Self::OrAssign => write!(f, "|="),
Self::XorAssign => write!(f, "^="),
Self::ShlAssign => write!(f, "<<="),
Self::ShrAssign => write!(f, ">>="),
Self::Eq => write!(f, "=="),
Self::Ne => write!(f, "!="),
Self::Lt => write!(f, "<"),
Self::Gt => write!(f, ">"),
Self::Le => write!(f, "<="),
Self::Ge => write!(f, ">="),
Self::LogicalAnd => write!(f, "&&"),
Self::LogicalOr => write!(f, "||"),
Self::Comma => write!(f, ","),
}
}
}
impl FoldExpressionEvaluator {
pub fn unary_left(op: FoldOperator, pack: Vec<String>) -> Self {
Self {
operator: op,
is_left_fold: true,
pack,
init: None,
}
}
pub fn unary_right(op: FoldOperator, pack: Vec<String>) -> Self {
Self {
operator: op,
is_left_fold: false,
pack,
init: None,
}
}
pub fn binary_left(op: FoldOperator, pack: Vec<String>, init: &str) -> Self {
Self {
operator: op,
is_left_fold: true,
pack,
init: Some(init.to_string()),
}
}
pub fn binary_right(op: FoldOperator, pack: Vec<String>, init: &str) -> Self {
Self {
operator: op,
is_left_fold: false,
pack,
init: Some(init.to_string()),
}
}
pub fn evaluate(&self) -> Option<String> {
if self.pack.is_empty() {
return self.identity_value();
}
if self.pack.len() == 1 {
return Some(self.pack[0].clone());
}
let mut result = String::new();
result.push('(');
if self.is_left_fold {
if let Some(ref init) = self.init {
result.push_str(&format!("{} {} ", init, self.operator));
}
result.push_str("...");
result.push_str(&format!(" {} ", self.operator));
result.push_str(&self.pack.join(&format!(" {} ", self.operator)));
} else {
result.push_str(&self.pack.join(&format!(" {} ", self.operator)));
result.push_str(&format!(" {} ...", self.operator));
if let Some(ref init) = self.init {
result.push_str(&format!(" {} {}", self.operator, init));
}
}
result.push(')');
Some(result)
}
fn identity_value(&self) -> Option<String> {
match self.operator {
FoldOperator::Add | FoldOperator::AddAssign => Some("0".to_string()),
FoldOperator::Mul | FoldOperator::MulAssign => Some("1".to_string()),
FoldOperator::BitAnd | FoldOperator::AndAssign => Some("-1".to_string()),
FoldOperator::BitOr | FoldOperator::OrAssign => Some("0".to_string()),
FoldOperator::LogicalAnd => Some("true".to_string()),
FoldOperator::LogicalOr => Some("false".to_string()),
FoldOperator::Comma => Some("void()".to_string()),
_ => None,
}
}
}
#[derive(Debug, Clone)]
pub struct ConstrainedTemplateParam {
pub param: DeepTemplateParam,
pub constraint: TemplateConstraint,
pub is_satisfied: bool,
}
impl ConstrainedTemplateParam {
pub fn new(param: DeepTemplateParam, constraint: TemplateConstraint) -> Self {
Self {
param,
constraint,
is_satisfied: false,
}
}
pub fn check_constraint(&mut self, args: &HashMap<String, DeepTemplateArg>) -> bool {
self.is_satisfied = self.constraint.is_satisfied(args);
self.is_satisfied
}
}
#[derive(Debug, Clone)]
pub struct StructuralNonTypeArg {
pub value: String,
pub ty: QualType,
pub is_null_pointer: bool,
pub is_integral: bool,
pub is_enum: bool,
pub is_floating_point: bool,
pub is_pointer: bool,
pub is_pointer_to_member: bool,
pub is_lvalue_ref: bool,
}
impl StructuralNonTypeArg {
pub fn integral(value: &str, ty: QualType) -> Self {
Self {
value: value.to_string(),
ty,
is_null_pointer: false,
is_integral: true,
is_enum: false,
is_floating_point: false,
is_pointer: false,
is_pointer_to_member: false,
is_lvalue_ref: false,
}
}
pub fn null_pointer(ty: QualType) -> Self {
Self {
value: "nullptr".to_string(),
ty,
is_null_pointer: true,
is_integral: false,
is_enum: false,
is_floating_point: false,
is_pointer: true,
is_pointer_to_member: false,
is_lvalue_ref: false,
}
}
pub fn is_structural(&self) -> bool {
self.is_integral || self.is_enum || self.is_null_pointer || self.is_pointer
}
pub fn structural_equivalence(&self, other: &Self) -> bool {
if !self.is_structural() || !other.is_structural() {
return false;
}
self.value == other.value
}
}
#[derive(Debug, Clone)]
pub struct DeepTemplateArgCanonicalizer {
pub strip_typedefs: bool,
pub normalize_integral_values: bool,
pub canonical_cache: HashMap<String, DeepTemplateArg>,
}
impl DeepTemplateArgCanonicalizer {
pub fn new() -> Self {
Self {
strip_typedefs: true,
normalize_integral_values: true,
canonical_cache: HashMap::new(),
}
}
pub fn canonicalize(&mut self, arg: &DeepTemplateArg) -> DeepTemplateArg {
let key = format!("{}", arg);
if let Some(cached) = self.canonical_cache.get(&key) {
return cached.clone();
}
let canonical = match arg {
DeepTemplateArg::Type(ty) => {
if self.strip_typedefs {
DeepTemplateArg::Type(ty.clone())
} else {
arg.clone()
}
}
DeepTemplateArg::NonType(val) => {
if self.normalize_integral_values {
DeepTemplateArg::NonType(val.clone())
} else {
arg.clone()
}
}
_ => arg.clone(),
};
self.canonical_cache.insert(key, canonical.clone());
canonical
}
pub fn canonicalize_all(&mut self, args: &[DeepTemplateArg]) -> Vec<DeepTemplateArg> {
args.iter().map(|a| self.canonicalize(a)).collect()
}
}
impl Default for DeepTemplateArgCanonicalizer {
fn default() -> Self {
Self::new()
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum NameDependence {
NonDependent,
TypeDependent,
ValueDependent,
FullyDependent,
}
impl fmt::Display for NameDependence {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::NonDependent => write!(f, "non-dependent"),
Self::TypeDependent => write!(f, "type-dependent"),
Self::ValueDependent => write!(f, "value-dependent"),
Self::FullyDependent => write!(f, "fully-dependent"),
}
}
}
#[derive(Debug, Clone)]
pub struct TwoPhaseResolver {
pub phase: LookupPhase,
pub template_params: Vec<DeepTemplateParam>,
pub phase1_resolved: HashMap<String, Vec<LookupDeclaration>>,
pub deferred_names: Vec<DependentNameRef>,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum LookupPhase {
Phase1,
Phase2,
}
#[derive(Debug, Clone)]
pub struct DependentNameRef {
pub name: String,
pub scope: String,
pub dependence: NameDependence,
pub source_location: usize,
}
impl TwoPhaseResolver {
pub fn new(params: Vec<DeepTemplateParam>) -> Self {
Self {
phase: LookupPhase::Phase1,
template_params: params,
phase1_resolved: HashMap::new(),
deferred_names: Vec::new(),
}
}
pub fn enter_phase2(&mut self) {
self.phase = LookupPhase::Phase2;
}
pub fn classify_name(&mut self, name: &str, scope: &str) -> NameDependence {
let is_dependent = self
.template_params
.iter()
.any(|p| p.name() == name || scope.contains(p.name()));
if is_dependent {
NameDependence::TypeDependent
} else {
NameDependence::NonDependent
}
}
pub fn defer_lookup(&mut self, name: &str, scope: &str, dep: NameDependence, loc: usize) {
self.deferred_names.push(DependentNameRef {
name: name.to_string(),
scope: scope.to_string(),
dependence: dep,
source_location: loc,
});
}
pub fn resolve_deferred(
&self,
lookup: &DeepNameLookup,
_template_args: &HashMap<String, DeepTemplateArg>,
) -> Vec<LookupDeclaration> {
let mut results = Vec::new();
for deferred in &self.deferred_names {
if self.phase == LookupPhase::Phase2 {
let result = lookup.qualified_lookup(&deferred.scope, &deferred.name);
results.extend(result.declarations);
}
}
results
}
pub fn needs_typename_keyword(&self, name: &str) -> bool {
self.template_params.iter().any(|p| p.name() == name)
}
pub fn needs_template_keyword(&self, _name: &str, scope: &str) -> bool {
self.template_params
.iter()
.any(|p| scope.contains(p.name()))
}
}
impl Default for TwoPhaseResolver {
fn default() -> Self {
Self::new(Vec::new())
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum BuiltinCandidateKind {
Arithmetic(BinaryOp),
Relational(BinaryOp),
Equality(BinaryOp),
Logical(BinaryOp),
Bitwise(BinaryOp),
Assignment(BinaryOp),
Unary(UnaryOp),
Subscript,
Conditional,
Comma,
}
impl fmt::Display for BuiltinCandidateKind {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::Arithmetic(op) => write!(f, "builtin-arithmetic-{:?}", op),
Self::Relational(op) => write!(f, "builtin-relational-{:?}", op),
Self::Equality(op) => write!(f, "builtin-equality-{:?}", op),
Self::Logical(op) => write!(f, "builtin-logical-{:?}", op),
Self::Bitwise(op) => write!(f, "builtin-bitwise-{:?}", op),
Self::Assignment(op) => write!(f, "builtin-assignment-{:?}", op),
Self::Unary(op) => write!(f, "builtin-unary-{:?}", op),
Self::Subscript => write!(f, "builtin-subscript"),
Self::Conditional => write!(f, "builtin-conditional"),
Self::Comma => write!(f, "builtin-comma"),
}
}
}
#[derive(Debug, Clone)]
pub struct BuiltinOperatorCandidate {
pub kind: BuiltinCandidateKind,
pub left_type: QualType,
pub right_type: Option<QualType>,
pub result_type: QualType,
pub rank: DeepConversionRank,
}
impl BuiltinOperatorCandidate {
pub fn arithmetic(op: BinaryOp, left: QualType, right: QualType, result: QualType) -> Self {
Self {
kind: BuiltinCandidateKind::Arithmetic(op),
left_type: left,
right_type: Some(right),
result_type: result,
rank: DeepConversionRank::ExactMatch,
}
}
pub fn relational(op: BinaryOp, left: QualType, right: QualType) -> Self {
Self {
kind: BuiltinCandidateKind::Relational(op),
left_type: left,
right_type: Some(right),
result_type: QualType::bool_(),
rank: DeepConversionRank::ExactMatch,
}
}
pub fn unary(op: UnaryOp, operand: QualType, result: QualType) -> Self {
Self {
kind: BuiltinCandidateKind::Unary(op),
left_type: operand,
right_type: None,
result_type: result,
rank: DeepConversionRank::ExactMatch,
}
}
}
#[derive(Debug, Clone)]
pub struct BuiltinCandidateGenerator {
pub include_promoted: bool,
pub include_converted: bool,
}
impl BuiltinCandidateGenerator {
pub fn new() -> Self {
Self {
include_promoted: true,
include_converted: true,
}
}
pub fn generate_arithmetic_candidates(
&self,
lhs: &QualType,
rhs: &QualType,
op: BinaryOp,
) -> Vec<BuiltinOperatorCandidate> {
let mut candidates = Vec::new();
if lhs == rhs {
candidates.push(BuiltinOperatorCandidate::arithmetic(
op,
lhs.clone(),
rhs.clone(),
lhs.clone(),
));
}
if self.include_converted {
let converted = QualType::int(); candidates.push(BuiltinOperatorCandidate::arithmetic(
op,
converted.clone(),
converted.clone(),
converted,
));
}
candidates
}
pub fn generate_relational_candidates(
&self,
lhs: &QualType,
rhs: &QualType,
op: BinaryOp,
) -> Vec<BuiltinOperatorCandidate> {
let mut candidates = Vec::new();
if lhs == rhs {
candidates.push(BuiltinOperatorCandidate::relational(
op,
lhs.clone(),
rhs.clone(),
));
}
candidates
}
}
impl Default for BuiltinCandidateGenerator {
fn default() -> Self {
Self::new()
}
}
#[derive(Debug, Clone)]
pub struct ConstructorOverloadResolver {
pub class_name: String,
pub constructors: Vec<DeepOverloadCandidate>,
pub is_aggregate: bool,
pub has_initializer_list_constructor: bool,
}
impl ConstructorOverloadResolver {
pub fn new(class_name: &str) -> Self {
Self {
class_name: class_name.to_string(),
constructors: Vec::new(),
is_aggregate: false,
has_initializer_list_constructor: false,
}
}
pub fn add_constructor(&mut self, ctor: DeepOverloadCandidate) {
self.constructors.push(ctor);
}
pub fn resolve_direct_init(&self, arg_types: &[QualType]) -> Option<ConstructorResolution> {
if self.constructors.is_empty() {
if arg_types.is_empty() {
return Some(ConstructorResolution::DefaultConstructor);
}
return None;
}
let mut best: Option<(&DeepOverloadCandidate, Vec<DeepConversionSequence>)> = None;
for ctor in &self.constructors {
if ctor.param_types.len() != arg_types.len() && !ctor.is_variadic {
continue;
}
let mut seqs = Vec::new();
let mut all_viable = true;
for (i, param_ty) in ctor.param_types.iter().enumerate() {
if i >= arg_types.len() {
break;
}
let seq = DeepConversionSequence::exact_match(); if !seq.is_viable() {
all_viable = false;
break;
}
seqs.push(seq);
}
if all_viable {
best = Some((ctor, seqs));
break;
}
}
best.map(|(ctor, _seqs)| ConstructorResolution::ConstructorFound {
constructor_name: ctor.decl_name.clone(),
is_explicit: false,
})
}
pub fn resolve_list_init(&self, _element_types: &[QualType]) -> Option<ListInitResolution> {
if self.has_initializer_list_constructor {
return Some(ListInitResolution::InitializerListConstructor);
}
if self.is_aggregate {
return Some(ListInitResolution::AggregateInitialization);
}
Some(ListInitResolution::RegularConstructor)
}
}
#[derive(Debug, Clone)]
pub enum ConstructorResolution {
DefaultConstructor,
ConstructorFound {
constructor_name: String,
is_explicit: bool,
},
Ambiguous,
NoViableConstructor,
}
#[derive(Debug, Clone)]
pub enum ListInitResolution {
InitializerListConstructor,
RegularConstructor,
AggregateInitialization,
}
#[derive(Debug, Clone)]
pub struct ReferenceBindingRules {
pub ref_kind: ReferenceKind,
pub source_value_category: ValueCategory,
pub is_direct_binding: bool,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum ReferenceKind {
LValueRef,
ConstLValueRef,
RValueRef,
ConstRValueRef,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum ValueCategory {
LValue,
XValue,
PRvalue,
}
impl fmt::Display for ValueCategory {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::LValue => write!(f, "lvalue"),
Self::XValue => write!(f, "xvalue"),
Self::PRvalue => write!(f, "prvalue"),
}
}
}
impl ReferenceBindingRules {
pub fn new(ref_kind: ReferenceKind, source_cat: ValueCategory) -> Self {
Self {
ref_kind,
source_value_category: source_cat,
is_direct_binding: false,
}
}
pub fn is_valid(&self) -> bool {
match self.ref_kind {
ReferenceKind::LValueRef => {
matches!(self.source_value_category, ValueCategory::LValue)
}
ReferenceKind::ConstLValueRef => {
true
}
ReferenceKind::RValueRef => {
matches!(
self.source_value_category,
ValueCategory::XValue | ValueCategory::PRvalue
)
}
ReferenceKind::ConstRValueRef => true,
}
}
pub fn binding_rank(&self) -> DeepConversionRank {
if !self.is_valid() {
return DeepConversionRank::NotViable;
}
match self.ref_kind {
ReferenceKind::LValueRef => DeepConversionRank::ExactMatch,
ReferenceKind::RValueRef => DeepConversionRank::ExactMatch,
ReferenceKind::ConstLValueRef => DeepConversionRank::ExactMatch,
ReferenceKind::ConstRValueRef => DeepConversionRank::ExactMatch,
}
}
pub fn is_direct_binding(&self) -> bool {
match self.ref_kind {
ReferenceKind::LValueRef => self.source_value_category == ValueCategory::LValue,
ReferenceKind::RValueRef => {
matches!(
self.source_value_category,
ValueCategory::XValue | ValueCategory::PRvalue
)
}
_ => false,
}
}
}
#[derive(Debug, Clone)]
pub struct InjectedClassNameTracker {
pub current_class: Option<String>,
pub injected_names: HashMap<String, String>,
pub is_within_class_definition: bool,
}
impl InjectedClassNameTracker {
pub fn new() -> Self {
Self {
current_class: None,
injected_names: HashMap::new(),
is_within_class_definition: false,
}
}
pub fn enter_class(&mut self, class_name: &str) {
self.current_class = Some(class_name.to_string());
self.is_within_class_definition = true;
self.injected_names
.insert(class_name.to_string(), class_name.to_string());
}
pub fn leave_class(&mut self) {
if let Some(ref cn) = self.current_class.clone() {
self.injected_names.remove(cn);
}
self.current_class = None;
self.is_within_class_definition = false;
}
pub fn is_injected_class_name(&self, name: &str) -> bool {
self.current_class.as_deref() == Some(name) && self.is_within_class_definition
}
pub fn resolve_injected_name(&self, name: &str) -> Option<&String> {
self.injected_names.get(name)
}
}
impl Default for InjectedClassNameTracker {
fn default() -> Self {
Self::new()
}
}
#[derive(Debug, Clone)]
pub struct ElaboratedTypeLookup {
pub tag_kind: ElaboratedTagKind,
pub is_forward_declaration: bool,
pub introduces_typename: bool,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum ElaboratedTagKind {
Class,
Struct,
Union,
Enum,
}
impl fmt::Display for ElaboratedTagKind {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::Class => write!(f, "class"),
Self::Struct => write!(f, "struct"),
Self::Union => write!(f, "union"),
Self::Enum => write!(f, "enum"),
}
}
}
impl ElaboratedTypeLookup {
pub fn new(tag: ElaboratedTagKind) -> Self {
Self {
tag_kind: tag,
is_forward_declaration: false,
introduces_typename: false,
}
}
pub fn as_forward_decl(mut self) -> Self {
self.is_forward_declaration = true;
self.introduces_typename = true;
self
}
pub fn can_introduce_name(&self) -> bool {
self.introduces_typename || self.is_forward_declaration
}
}
#[derive(Debug, Clone)]
pub struct MemberLookupChain {
pub class_name: String,
pub base_classes: Vec<BaseClassInfo>,
pub lookup_cache: HashMap<String, Option<LookupDeclaration>>,
}
#[derive(Debug, Clone)]
pub struct BaseClassInfo {
pub name: String,
pub access: MemberAccess,
pub is_virtual: bool,
pub is_direct: bool,
pub offset_bytes: usize,
}
impl MemberLookupChain {
pub fn new(class_name: &str) -> Self {
Self {
class_name: class_name.to_string(),
base_classes: Vec::new(),
lookup_cache: HashMap::new(),
}
}
pub fn add_base(&mut self, base: BaseClassInfo) {
self.base_classes.push(base);
}
pub fn lookup_member(&self, name: &str, lookup: &DeepNameLookup) -> Option<LookupDeclaration> {
if let Some(cached) = self.lookup_cache.get(name) {
return cached.clone();
}
let own_result = lookup.qualified_lookup(&self.class_name, name);
if own_result.found && !own_result.declarations.is_empty() {
return Some(own_result.declarations[0].clone());
}
for base in &self.base_classes {
if let Some(decl) = self.lookup_member_in_base(name, &base.name, lookup) {
return Some(decl);
}
}
None
}
fn lookup_member_in_base(
&self,
name: &str,
base_name: &str,
lookup: &DeepNameLookup,
) -> Option<LookupDeclaration> {
let result = lookup.qualified_lookup(base_name, name);
if result.found && !result.declarations.is_empty() {
let mut decl = result.declarations[0].clone();
decl.is_accessible = self.is_base_accessible(base_name);
return Some(decl);
}
None
}
fn is_base_accessible(&self, base_name: &str) -> bool {
self.base_classes
.iter()
.find(|b| b.name == base_name)
.map(|b| b.access == MemberAccess::Public || b.access == MemberAccess::Protected)
.unwrap_or(false)
}
}
#[derive(Debug, Clone)]
pub struct AccessPath {
pub segments: Vec<AccessPathSegment>,
pub target_member: String,
}
#[derive(Debug, Clone)]
pub struct AccessPathSegment {
pub class_name: String,
pub access: MemberAccess,
pub is_virtual: bool,
}
impl AccessPath {
pub fn new(target: &str) -> Self {
Self {
segments: Vec::new(),
target_member: target.to_string(),
}
}
pub fn add_segment(&mut self, class_name: &str, access: MemberAccess, is_virtual: bool) {
self.segments.push(AccessPathSegment {
class_name: class_name.to_string(),
access,
is_virtual,
});
}
pub fn effective_access(&self) -> MemberAccess {
self.segments.iter().fold(MemberAccess::Public, |acc, seg| {
Self::min_access(acc, seg.access)
})
}
fn min_access(a: MemberAccess, b: MemberAccess) -> MemberAccess {
match (a, b) {
(MemberAccess::Private, _) | (_, MemberAccess::Private) => MemberAccess::Private,
(MemberAccess::Protected, _) | (_, MemberAccess::Protected) => MemberAccess::Protected,
(MemberAccess::Public, MemberAccess::Public) => MemberAccess::Public,
_ => MemberAccess::Public,
}
}
pub fn is_fully_accessible(&self) -> bool {
self.effective_access() == MemberAccess::Public
}
pub fn check_against(
&self,
context: &AccessContext,
friend_mgr: &FriendManager,
) -> AccessResult {
for segment in &self.segments {
if segment.access == MemberAccess::Private {
if !context.is_friend_of(&segment.class_name) {
return AccessResult::denied(format!(
"'{}' is a private base of '{}'",
self.target_member, segment.class_name
));
}
}
if segment.access == MemberAccess::Protected {
if !context.is_friend_of(&segment.class_name)
&& context.current_class.as_deref() != Some(&segment.class_name)
{
return AccessResult::denied(format!(
"'{}' is a protected base of '{}'",
self.target_member, segment.class_name
));
}
}
}
AccessResult::allowed()
}
}
#[derive(Debug, Clone)]
pub struct TemplateAccessChecker {
pub base_checker: AccessChecker,
pub template_params: Vec<DeepTemplateParam>,
pub defer_until_instantiation: bool,
pub deferred_access_checks: Vec<DeferredAccessCheck>,
}
#[derive(Debug, Clone)]
pub struct DeferredAccessCheck {
pub member_name: String,
pub class_name: String,
pub access: MemberAccess,
pub source_location: usize,
}
impl TemplateAccessChecker {
pub fn new(context: AccessContext, params: Vec<DeepTemplateParam>) -> Self {
let defer = !params.is_empty();
TemplateAccessChecker {
base_checker: AccessChecker::new(context),
template_params: params,
defer_until_instantiation: defer,
deferred_access_checks: Vec::new(),
}
}
pub fn check_or_defer(
&mut self,
access: MemberAccess,
class_name: &str,
member_name: &str,
loc: usize,
) -> AccessResult {
if self.defer_until_instantiation {
self.deferred_access_checks.push(DeferredAccessCheck {
member_name: member_name.to_string(),
class_name: class_name.to_string(),
access,
source_location: loc,
});
AccessResult::allowed() } else {
self.base_checker
.check_access(access, class_name, member_name)
}
}
pub fn perform_deferred_checks(&self) -> Vec<AccessResult> {
self.deferred_access_checks
.iter()
.map(|dc| {
self.base_checker
.check_access(dc.access, &dc.class_name, &dc.member_name)
})
.collect()
}
pub fn instantiation_complete(&mut self) {
self.defer_until_instantiation = false;
self.deferred_access_checks.clear();
}
}
#[derive(Debug, Clone)]
pub struct UsingDeclAccess {
pub using_declaration: String,
pub original_access: MemberAccess,
pub access_in_derived: MemberAccess,
pub derives_from_access_specifier: bool,
}
impl UsingDeclAccess {
pub fn new(decl_name: &str, original: MemberAccess) -> Self {
Self {
using_declaration: decl_name.to_string(),
original_access: original,
access_in_derived: original,
derives_from_access_specifier: false,
}
}
pub fn adjust_to_access_specifier(&mut self, spec_access: MemberAccess) {
self.access_in_derived = spec_access;
self.derives_from_access_specifier = true;
}
pub fn is_access_changed(&self) -> bool {
self.original_access != self.access_in_derived
}
pub fn effective_access(&self) -> MemberAccess {
if self.derives_from_access_specifier {
self.access_in_derived
} else {
self.original_access
}
}
}
#[derive(Debug, Clone)]
pub struct GenericLambdaTemplate {
pub template_params: Vec<DeepTemplateParam>,
pub call_operator: DeepOverloadCandidate,
pub is_abbreviated_function_template: bool,
}
impl GenericLambdaTemplate {
pub fn from_lambda_params(params: &[(String, QualType)]) -> Self {
let mut template_params = Vec::new();
let mut synthetic_names = Vec::new();
for (name, ty) in params {
let ty_str = format!("{}", ty);
if ty_str == "auto" {
let tparam_name = format!("__T_{}", name);
template_params.push(DeepTemplateParam::type_param(&tparam_name, false));
synthetic_names.push((name.clone(), tparam_name));
}
}
Self {
template_params,
call_operator: DeepOverloadCandidate::new("operator()", vec![], false),
is_abbreviated_function_template: true,
}
}
pub fn instantiate_call_operator(
&self,
args: &[QualType],
) -> Result<DeepOverloadCandidate, String> {
Ok(self.call_operator.clone())
}
pub fn num_template_params(&self) -> usize {
self.template_params.len()
}
}
#[derive(Debug, Clone)]
pub struct LambdaConversionOperator {
pub target_function_type: String,
pub is_noexcept: bool,
pub is_constexpr: bool,
pub return_type: Option<QualType>,
pub param_types: Vec<QualType>,
}
impl LambdaConversionOperator {
pub fn new(return_ty: Option<QualType>, params: Vec<QualType>) -> Self {
Self {
target_function_type: String::new(),
is_noexcept: true,
is_constexpr: true,
return_type: return_ty,
param_types: params,
}
}
pub fn generate_conversion_ir(&self, original_closure_name: &str) -> String {
let mut s = String::new();
s.push_str(&format!(
"define ptr @{}_to_fptr() {{\n",
original_closure_name
));
s.push_str("entry:\n");
s.push_str(&format!(" ret ptr @{}_invoker\n", original_closure_name));
s.push_str("}\n");
s
}
pub fn generate_invoker_ir(&self, original_closure_name: &str) -> String {
let mut s = String::new();
s.push_str(&format!(
"define void @{}_invoker() {{\n",
original_closure_name
));
s.push_str("entry:\n");
s.push_str(&format!(
" call void @{}_operator_call()\n",
original_closure_name
));
s.push_str(" ret void\n");
s.push_str("}\n");
s
}
}
#[derive(Debug, Clone)]
pub struct UnevaluatedLambda {
pub lambda: DeepLambdaExpression,
pub context: UnevaluatedContext,
pub is_valid: bool,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum UnevaluatedContext {
Decltype,
Sizeof,
Alignof,
Noexcept,
Typeid,
RequiresExpression,
}
impl fmt::Display for UnevaluatedContext {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::Decltype => write!(f, "decltype"),
Self::Sizeof => write!(f, "sizeof"),
Self::Alignof => write!(f, "alignof"),
Self::Noexcept => write!(f, "noexcept"),
Self::Typeid => write!(f, "typeid"),
Self::RequiresExpression => write!(f, "requires-expression"),
}
}
}
impl UnevaluatedLambda {
pub fn new(lambda: DeepLambdaExpression, context: UnevaluatedContext) -> Self {
let is_valid = matches!(lambda.standard, CLangStandard::C23 | CLangStandard::Gnu17);
Self {
lambda,
context,
is_valid,
}
}
pub fn is_allowed(&self) -> bool {
self.is_valid
}
}
#[derive(Debug, Clone)]
pub struct NoexceptPropagation {
pub current_function_is_noexcept: bool,
pub has_potential_throw: bool,
pub noexcept_call_sites: Vec<NoexceptCallSite>,
}
#[derive(Debug, Clone)]
pub struct NoexceptCallSite {
pub callee_name: String,
pub callee_is_noexcept: bool,
pub location: String,
pub needs_noexcept_wrapper: bool,
}
impl NoexceptPropagation {
pub fn new(is_noexcept: bool) -> Self {
Self {
current_function_is_noexcept: is_noexcept,
has_potential_throw: false,
noexcept_call_sites: Vec::new(),
}
}
pub fn register_call(&mut self, callee: &str, callee_noexcept: bool, location: &str) {
let needs_wrapper = self.current_function_is_noexcept && !callee_noexcept;
if !callee_noexcept {
self.has_potential_throw = true;
}
self.noexcept_call_sites.push(NoexceptCallSite {
callee_name: callee.to_string(),
callee_is_noexcept: callee_noexcept,
location: location.to_string(),
needs_noexcept_wrapper: needs_wrapper,
});
}
pub fn requires_termination_on_throw(&self) -> bool {
self.current_function_is_noexcept && self.has_potential_throw
}
pub fn generate_noexcept_wrapper_header(&self) -> String {
if !self.requires_termination_on_throw() {
return String::new();
}
let mut s = String::new();
s.push_str("// noexcept wrapper:\n");
s.push_str("invoke void @__clang_call_terminate()\n");
s.push_str(" to label %normal unwind label %terminate_lpad\n");
s
}
}
impl Default for NoexceptPropagation {
fn default() -> Self {
Self::new(false)
}
}
#[derive(Debug, Clone)]
pub struct ExceptionPtr {
pub is_valid: bool,
pub exception_type: String,
pub type_info_symbol: String,
pub exception_object_ptr: Option<String>,
}
impl ExceptionPtr {
pub fn null() -> Self {
Self {
is_valid: false,
exception_type: String::new(),
type_info_symbol: String::new(),
exception_object_ptr: None,
}
}
pub fn from_current_exception(ty: &str, typeinfo: &str) -> Self {
Self {
is_valid: true,
exception_type: ty.to_string(),
type_info_symbol: typeinfo.to_string(),
exception_object_ptr: Some(format!("@exception_obj_{}", ty)),
}
}
pub fn rethrow_if_valid(&self) -> bool {
self.is_valid
}
pub fn generate_null_check_ir(&self, label: &str) -> String {
format!(" %{} = icmp eq ptr %exception_ptr, null\n", label)
}
}
#[derive(Debug, Clone)]
pub struct ExceptionTypeMatcher {
pub thrown_type_info: String,
pub catch_type_info: String,
pub hierarchy: HashMap<String, Vec<String>>,
pub this_adjustment: isize,
}
impl ExceptionTypeMatcher {
pub fn new(thrown_type: &str, catch_type: &str) -> Self {
Self {
thrown_type_info: thrown_type.to_string(),
catch_type_info: catch_type.to_string(),
hierarchy: HashMap::new(),
this_adjustment: 0,
}
}
pub fn add_base_class(&mut self, derived: &str, base: &str) {
self.hierarchy
.entry(derived.to_string())
.or_default()
.push(base.to_string());
}
pub fn matches(&self) -> bool {
if self.thrown_type_info == self.catch_type_info {
return true;
}
let mut to_visit: Vec<&str> = vec![&self.thrown_type_info];
while let Some(current) = to_visit.pop() {
if current == self.catch_type_info {
return true;
}
if let Some(bases) = self.hierarchy.get(current) {
to_visit.extend(bases.iter().map(|s| s.as_str()));
}
}
false
}
pub fn compute_this_adjustment(&mut self, base_offsets: &HashMap<String, isize>) {
self.this_adjustment = base_offsets
.get(&self.catch_type_info)
.copied()
.unwrap_or(0);
}
}
#[derive(Debug, Clone)]
pub struct ClassHierarchyDescriptor {
pub class_name: String,
pub type_info: DeepTypeInfo,
pub base_classes: Vec<BaseClassRttiInfo>,
pub flags: RttiBaseFlags,
}
#[derive(Debug, Clone)]
pub struct BaseClassRttiInfo {
pub base_type_info: DeepTypeInfo,
pub offset_flags: u64,
pub offset_to_base: isize,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct RttiBaseFlags {
pub is_virtual: bool,
pub is_public: bool,
pub is_protected: bool,
pub is_private: bool,
}
impl RttiBaseFlags {
pub const NONE: Self = Self {
is_virtual: false,
is_public: false,
is_protected: false,
is_private: false,
};
pub const VIRTUAL: u64 = 1;
pub const PUBLIC: u64 = 2;
pub const PROTECTED: u64 = 4;
pub const PRIVATE: u64 = 8;
pub fn from_access(access: MemberAccess, is_virtual: bool) -> Self {
Self {
is_virtual,
is_public: access == MemberAccess::Public,
is_protected: access == MemberAccess::Protected,
is_private: access == MemberAccess::Private,
}
}
pub fn to_flags(&self) -> u64 {
let mut f = 0u64;
if self.is_virtual {
f |= Self::VIRTUAL;
}
if self.is_public {
f |= Self::PUBLIC;
}
if self.is_protected {
f |= Self::PROTECTED;
}
if self.is_private {
f |= Self::PRIVATE;
}
f
}
pub fn from_flags(flags: u64) -> Self {
Self {
is_virtual: (flags & Self::VIRTUAL) != 0,
is_public: (flags & Self::PUBLIC) != 0,
is_protected: (flags & Self::PROTECTED) != 0,
is_private: (flags & Self::PRIVATE) != 0,
}
}
}
impl ClassHierarchyDescriptor {
pub fn new(class_name: &str, type_info: DeepTypeInfo) -> Self {
Self {
class_name: class_name.to_string(),
type_info,
base_classes: Vec::new(),
flags: RttiBaseFlags::NONE,
}
}
pub fn add_base(&mut self, base: BaseClassRttiInfo) {
self.base_classes.push(base);
}
pub fn emit_si_class_type_info(&self) -> String {
let mut s = String::new();
s.push_str(&format!(
"@_ZTSI{} = linkonce_odr constant {{ ptr, ptr }} {{\n",
self.class_name
));
s.push_str(&format!(
" ptr @_ZTVN10__cxxabiv120__si_class_type_infoE,\n"
));
s.push_str(&format!(" ptr @_ZTS{}\n", self.class_name));
s.push_str("}\n");
s
}
pub fn emit_vmi_class_type_info(&self) -> String {
let mut s = String::new();
s.push_str(&format!(
"@_ZTSV{} = linkonce_odr constant {{ ptr, i32, i32, ptr }} {{\n",
self.class_name
));
s.push_str(&format!(
" ptr @_ZTVN10__cxxabiv121__vmi_class_type_infoE,\n"
));
s.push_str(&format!(" i32 {},\n", self.flags.to_flags()));
s.push_str(&format!(" i32 {},\n", self.base_classes.len()));
s.push_str(&format!(" ptr @_ZTS{}_base_array\n", self.class_name));
s.push_str("}\n");
s
}
pub fn emit_base_array(&self) -> String {
let mut s = String::new();
s.push_str(&format!(
"@_ZTS{}_base_array = linkonce_odr constant [{} x {{ ptr, i64 }}] [\n",
self.class_name,
self.base_classes.len()
));
for (i, base) in self.base_classes.iter().enumerate() {
if i > 0 {
s.push_str(",\n");
}
s.push_str(&format!(
" {{ ptr @_ZTS{}, i64 {} }}",
base.base_type_info.type_name,
base.offset_flags | (base.offset_to_base as u64)
));
}
s.push_str("\n]\n");
s
}
}
#[derive(Debug, Clone)]
pub struct PointerToMemberTypeInfo {
pub class_type_info: String,
pub member_type_info: String,
pub pointer_type_info: String,
}
impl PointerToMemberTypeInfo {
pub fn new(class_ti: &str, member_ti: &str) -> Self {
Self {
class_type_info: class_ti.to_string(),
member_type_info: member_ti.to_string(),
pointer_type_info: format!("_ZTSPM{}M{}", class_ti, member_ti),
}
}
pub fn emit_ir(&self) -> String {
format!(
"@{} = linkonce_odr constant {{ ptr, ptr, ptr }} {{\n\
\x20 ptr @_ZTVN10__cxxabiv129__pointer_to_member_type_infoE,\n\
\x20 ptr @{},\n\
\x20 ptr @{}\n\
}}\n",
self.pointer_type_info, self.class_type_info, self.member_type_info
)
}
}
#[derive(Debug, Clone)]
pub struct ConstructionVtable {
pub context_class: String,
pub target_class: String,
pub vtable_entries: Vec<DeepVtableEntry>,
pub vtable_name: String,
}
impl ConstructionVtable {
pub fn new(context: &str, target: &str) -> Self {
Self {
context_class: context.to_string(),
target_class: target.to_string(),
vtable_entries: Vec::new(),
vtable_name: format!("_ZTC{}{}", context, target),
}
}
pub fn add_entry(&mut self, entry: DeepVtableEntry) {
self.vtable_entries.push(entry);
}
pub fn needs_construction_vtable(&self) -> bool {
self.context_class != self.target_class
}
pub fn emit_ir(&self) -> String {
let mut s = String::new();
s.push_str(&format!(
"@{} = linkonce_odr constant [{} x ptr] [\n",
self.vtable_name,
self.vtable_entries.len(),
));
for (i, entry) in self.vtable_entries.iter().enumerate() {
if i > 0 {
s.push_str(",\n");
}
s.push_str(&format!(" ptr {}", entry));
}
s.push_str("\n]\n");
s
}
}
#[derive(Debug, Clone)]
pub struct VttBuilder {
pub class_name: String,
pub primary_vtable_name: String,
pub construction_vtables: Vec<ConstructionVtable>,
pub secondary_vtable_pointers: Vec<String>,
}
impl VttBuilder {
pub fn new(class_name: &str) -> Self {
Self {
class_name: class_name.to_string(),
primary_vtable_name: format!("_ZTV{}", class_name),
construction_vtables: Vec::new(),
secondary_vtable_pointers: Vec::new(),
}
}
pub fn add_construction_vtable(&mut self, cv: ConstructionVtable) {
self.construction_vtables.push(cv);
}
pub fn add_secondary_vtable_ptr(&mut self, ptr_name: &str) {
self.secondary_vtable_pointers.push(ptr_name.to_string());
}
pub fn build(&self) -> String {
let mut s = String::new();
let vtt_name = format!("_ZTT{}", self.class_name);
let mut entries: Vec<String> = Vec::new();
entries.push(self.primary_vtable_name.clone());
for cv in &self.construction_vtables {
entries.push(cv.vtable_name.clone());
}
entries.extend(self.secondary_vtable_pointers.clone());
s.push_str(&format!(
"@{} = linkonce_odr constant [{} x ptr] [\n",
vtt_name,
entries.len()
));
for (i, e) in entries.iter().enumerate() {
if i > 0 {
s.push_str(",\n");
}
s.push_str(&format!(" ptr {}", e));
}
s.push_str("\n]\n");
s
}
}
#[derive(Debug, Clone)]
pub struct CovariantReturnThunk {
pub thunk_name: String,
pub target_function: String,
pub return_type_base: QualType,
pub return_type_derived: QualType,
pub return_adjustment_bytes: isize,
pub this_adjustment_bytes: isize,
}
impl CovariantReturnThunk {
pub fn new(
target: &str,
ret_base: QualType,
ret_derived: QualType,
ret_adjustment: isize,
this_adjustment: isize,
) -> Self {
Self {
thunk_name: format!("__covariant_thunk_{}", target),
target_function: target.to_string(),
return_type_base: ret_base,
return_type_derived: ret_derived,
return_adjustment_bytes: ret_adjustment,
this_adjustment_bytes: this_adjustment,
}
}
pub fn generate_ir(&self) -> String {
let mut s = String::new();
s.push_str(&format!("define ptr @{}(ptr %this) {{\n", self.thunk_name));
s.push_str("entry:\n");
if self.this_adjustment_bytes != 0 {
s.push_str(&format!(
" %this_adj = getelementptr i8, ptr %this, i64 {}\n",
self.this_adjustment_bytes
));
}
s.push_str(&format!(
" %ret = call ptr @{}(ptr %this)\n",
self.target_function
));
if self.return_adjustment_bytes != 0 {
s.push_str(&format!(
" %ret_adj = getelementptr i8, ptr %ret, i64 {}\n",
self.return_adjustment_bytes
));
s.push_str(" ret ptr %ret_adj\n");
} else {
s.push_str(" ret ptr %ret\n");
}
s.push_str("}\n");
s
}
}
#[derive(Debug, Clone)]
pub struct MicrosoftVtableLayout {
pub class_name: String,
pub complete_object_locator_ptr: String,
pub virtual_functions: Vec<String>,
pub has_vbase_descriptor: bool,
}
impl MicrosoftVtableLayout {
pub fn new(class_name: &str) -> Self {
Self {
class_name: class_name.to_string(),
complete_object_locator_ptr: format!("??_R4{}@@6B@", class_name),
virtual_functions: Vec::new(),
has_vbase_descriptor: false,
}
}
pub fn add_virtual_function(&mut self, name: &str) {
self.virtual_functions.push(name.to_string());
}
pub fn emit_ir(&self) -> String {
let mut s = String::new();
s.push_str(&format!(
"@??_7{}@@6B@ = linkonce_odr constant [{} x ptr] [\n",
self.class_name,
self.virtual_functions.len() + 1,
));
s.push_str(&format!(" ptr {},\n", self.complete_object_locator_ptr));
for (i, vf) in self.virtual_functions.iter().enumerate() {
if i > 0 {
s.push_str(",\n");
}
s.push_str(&format!(" ptr {}", vf));
}
s.push_str("\n]\n");
s
}
}
#[derive(Debug, Clone)]
pub struct GuardVariable {
pub guard_name: String,
pub guarded_variable_name: String,
pub is_64bit: bool,
pub is_initialized: bool,
}
impl GuardVariable {
pub const GUARD_INIT: u8 = 0;
pub const GUARD_DONE: u8 = 1;
pub const GUARD_ACQUIRED: u8 = 2;
pub fn new(var_name: &str) -> Self {
Self {
guard_name: format!("_ZGV{}", var_name),
guarded_variable_name: var_name.to_string(),
is_64bit: true,
is_initialized: false,
}
}
pub fn generate_init_guard_ir(&self) -> String {
let mut s = String::new();
s.push_str(&format!("@{} = internal global i64 0\n", self.guard_name));
s.push_str(&format!(
"define void @__cxx_global_var_init_{}() {{\n",
self.guarded_variable_name
));
s.push_str("entry:\n");
s.push_str(&format!(" %guard = load i64, ptr @{}\n", self.guard_name));
s.push_str(&format!(" %is_init = icmp eq i64 %guard, 1\n"));
s.push_str(" br i1 %is_init, label %done, label %init\n\n");
s.push_str("init:\n");
s.push_str(&format!(
" %acquired = call i32 @__cxa_guard_acquire(ptr @{})\n",
self.guard_name
));
s.push_str(" %should_init = icmp ne i32 %acquired, 0\n");
s.push_str(" br i1 %should_init, label %do_init, label %done\n\n");
s.push_str("do_init:\n");
s.push_str(&format!(
" ; initialize {} here\n",
self.guarded_variable_name
));
s.push_str(&format!(
" call void @__cxa_guard_release(ptr @{})\n",
self.guard_name
));
s.push_str(" br label %done\n\n");
s.push_str("done:\n");
s.push_str(" ret void\n");
s.push_str("}\n");
s
}
pub fn generate_guard_abort_ir(&self) -> String {
format!(
"define void @__cxx_global_var_init_abort_{}() {{\n\
entry:\n\
\x20 call void @__cxa_guard_abort(ptr @{})\n\
\x20 ret void\n\
}}\n",
self.guarded_variable_name, self.guard_name
)
}
}
#[derive(Debug, Clone)]
pub struct ComdatManager {
pub comdat_groups: HashMap<String, ComdatGroup>,
pub use_comdat: bool,
}
#[derive(Debug, Clone)]
pub struct ComdatGroup {
pub name: String,
pub selection_kind: ComdatSelectionKind,
pub members: Vec<String>,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum ComdatSelectionKind {
Any,
ExactMatch,
Largest,
NoDuplicates,
SameSize,
}
impl fmt::Display for ComdatSelectionKind {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::Any => write!(f, "any"),
Self::ExactMatch => write!(f, "exactmatch"),
Self::Largest => write!(f, "largest"),
Self::NoDuplicates => write!(f, "noduplicates"),
Self::SameSize => write!(f, "samesize"),
}
}
}
impl ComdatManager {
pub fn new(use_comdat: bool) -> Self {
Self {
comdat_groups: HashMap::new(),
use_comdat,
}
}
pub fn create_comdat(&mut self, name: &str, kind: ComdatSelectionKind) -> String {
let group = ComdatGroup {
name: name.to_string(),
selection_kind: kind,
members: Vec::new(),
};
self.comdat_groups.insert(name.to_string(), group);
name.to_string()
}
pub fn add_member(&mut self, group_name: &str, member_name: &str) {
if let Some(group) = self.comdat_groups.get_mut(group_name) {
group.members.push(member_name.to_string());
}
}
pub fn emit_comdat_decl(&self, group_name: &str) -> String {
if let Some(group) = self.comdat_groups.get(group_name) {
format!("$comdat {} = comdat {}", group.name, group.selection_kind)
} else {
String::new()
}
}
pub fn vtable_comdat(&mut self, class_name: &str) -> String {
let group_name = format!("_ZTV{}_comdat", class_name);
self.create_comdat(&group_name, ComdatSelectionKind::Largest);
self.add_member(&group_name, &format!("_ZTV{}", class_name));
self.add_member(&group_name, &format!("_ZTI{}", class_name));
self.add_member(&group_name, &format!("_ZTS{}", class_name));
group_name
}
}
impl Default for ComdatManager {
fn default() -> Self {
Self::new(true)
}
}
#[derive(Debug, Clone)]
pub struct ThreadSafeStaticInit {
pub guard_variable: GuardVariable,
pub initializer_function: String,
pub is_thread_local: bool,
}
impl ThreadSafeStaticInit {
pub fn new(var_name: &str, init_fn: &str) -> Self {
Self {
guard_variable: GuardVariable::new(var_name),
initializer_function: init_fn.to_string(),
is_thread_local: false,
}
}
pub fn thread_local(mut self) -> Self {
self.is_thread_local = true;
self
}
pub fn generate_complete_ir(&self) -> String {
let mut s = String::new();
s.push_str(&self.guard_variable.generate_init_guard_ir());
s.push_str(&format!(
"@llvm.global_ctors = appending global [1 x {{ i32, ptr, ptr }}] [\n\
\x20 {{ i32, ptr, ptr }} {{ i32 65535, ptr @{0}, ptr null }}\n\
]\n",
self.initializer_function
));
s
}
}
#[derive(Debug, Clone)]
pub struct ItaniumAbiHelpers {
pub personality_function: String,
pub terminate_handler: String,
pub unexpected_handler: String,
pub new_handler: String,
pub pure_virtual_handler: String,
pub deleted_virtual_handler: String,
}
impl ItaniumAbiHelpers {
pub fn standard() -> Self {
Self {
personality_function: "__gxx_personality_v0".to_string(),
terminate_handler: "_ZSt9terminatev".to_string(),
unexpected_handler: "_ZSt10unexpectedv".to_string(),
new_handler: "_Znwm".to_string(),
pure_virtual_handler: "__cxa_pure_virtual".to_string(),
deleted_virtual_handler: "__cxa_deleted_virtual".to_string(),
}
}
pub fn emit_declarations(&self) -> String {
let mut s = String::new();
s.push_str(&format!(
"declare i32 @{}(...)\n",
self.personality_function
));
s.push_str(&format!("declare void @{}()\n", self.terminate_handler));
s.push_str(&format!("declare ptr @{}(i64)\n", self.new_handler));
s.push_str(&format!("declare void @{}()\n", self.pure_virtual_handler));
s
}
}
#[cfg(test)]
mod tests {
use super::*;
fn int_ty() -> QualType {
QualType::int()
}
fn float_ty() -> QualType {
QualType::float_()
}
fn double_ty() -> QualType {
QualType::double_()
}
fn void_ty() -> QualType {
QualType::void()
}
fn char_ty() -> QualType {
QualType::char()
}
#[test]
fn test_template_param_type_param() {
let p = DeepTemplateParam::type_param("T", false);
assert_eq!(p.name(), "T");
assert!(!p.is_parameter_pack());
assert!(!p.has_default());
assert_eq!(format!("{}", p), "typename T");
}
#[test]
fn test_template_param_pack() {
let p = DeepTemplateParam::type_param("Args", true);
assert_eq!(p.name(), "Args");
assert!(p.is_parameter_pack());
assert_eq!(format!("{}", p), "typename... Args");
}
#[test]
fn test_template_arg_type() {
let arg = DeepTemplateArg::type_arg(int_ty());
match &arg {
DeepTemplateArg::Type(ty) => assert_eq!(format!("{}", ty), "int"),
_ => panic!("expected Type"),
}
}
#[test]
fn test_instantiation_depth_tracker_basic() {
let mut tracker = InstantiationDepthTracker::with_default_max();
assert_eq!(tracker.depth(), 0);
assert!(tracker.enter("vector", &[]));
assert_eq!(tracker.depth(), 1);
tracker.leave();
assert_eq!(tracker.depth(), 0);
}
#[test]
fn test_instantiation_depth_tracker_max() {
let mut tracker = InstantiationDepthTracker::new(3);
assert!(tracker.enter("A", &[]));
assert!(tracker.enter("B", &[]));
assert!(tracker.enter("C", &[]));
assert!(!tracker.enter("D", &[])); assert_eq!(tracker.depth(), 3);
}
#[test]
fn test_instantiation_depth_backtrace() {
let mut tracker = InstantiationDepthTracker::with_default_max();
tracker.enter("vector", &[DeepTemplateArg::type_arg(int_ty())]);
tracker.enter("allocator", &[]);
let bt = tracker.backtrace_string();
assert!(bt.contains("vector"));
assert!(bt.contains("allocator"));
}
#[test]
fn test_sfinae_state() {
let mut tracker = InstantiationDepthTracker::with_default_max();
assert!(!tracker.sfinae_active());
tracker.enter_sfinae();
assert!(tracker.sfinae_active());
tracker.record_sfinae_failure();
assert!(tracker.sfinae_state.has_failed());
tracker.leave_sfinae();
assert!(!tracker.sfinae_active());
}
#[test]
fn test_deduction_result_success() {
let mut args = HashMap::new();
args.insert("T".to_string(), DeepTemplateArg::type_arg(int_ty()));
let result = DeepDeductionResult::success(args);
assert!(result.success);
assert!(!result.substitution_failure);
}
#[test]
fn test_deduction_result_failure() {
let result = DeepDeductionResult::failure("type mismatch");
assert!(!result.success);
assert_eq!(result.failure_reason.unwrap(), "type mismatch");
}
#[test]
fn test_deduction_result_sfinae() {
let result = DeepDeductionResult::sfinae_failure("substitution failed");
assert!(!result.success);
assert!(result.substitution_failure);
}
#[test]
fn test_template_specialization_exact_match() {
let mut matcher = TemplateSpecializationMatcher::new("vector");
matcher
.add_explicit_specialization(vec![DeepTemplateArg::type_arg(int_ty())], "vector_int");
let result = matcher.find_best_match(&[DeepTemplateArg::type_arg(int_ty())]);
assert_eq!(result, Some("vector_int".to_string()));
}
#[test]
fn test_template_specialization_no_match() {
let matcher = TemplateSpecializationMatcher::new("vector");
let result = matcher.find_best_match(&[DeepTemplateArg::type_arg(int_ty())]);
assert!(result.is_none());
}
#[test]
fn test_template_instantiator_cache() {
let mut inst = DeepTemplateInstantiator::new("vector");
let args = vec![DeepTemplateArg::type_arg(int_ty())];
let r1 = inst.instantiate("vector", &args).unwrap();
let r2 = inst.instantiate("vector", &args).unwrap();
assert_eq!(r1, r2); }
#[test]
fn test_template_constraint() {
let c = TemplateConstraint::new("integral", vec![], "is_integral_v<T>");
assert_eq!(c.name, "integral");
assert!(c.is_satisfied(&HashMap::new()));
}
#[test]
fn test_conversion_rank_ordering() {
assert!(DeepConversionRank::ExactMatch < DeepConversionRank::Promotion);
assert!(DeepConversionRank::Promotion < DeepConversionRank::StandardConversion);
assert!(DeepConversionRank::StandardConversion < DeepConversionRank::UserDefinedConversion);
assert!(DeepConversionRank::UserDefinedConversion < DeepConversionRank::Ellipsis);
assert!(DeepConversionRank::Ellipsis < DeepConversionRank::NotViable);
}
#[test]
fn test_conversion_sequence_exact_match() {
let seq = DeepConversionSequence::exact_match();
assert!(seq.is_viable());
assert_eq!(seq.rank, DeepConversionRank::ExactMatch);
}
#[test]
fn test_conversion_sequence_not_viable() {
let seq = DeepConversionSequence::not_viable();
assert!(!seq.is_viable());
}
#[test]
fn test_overload_candidate_new() {
let cand = DeepOverloadCandidate::new("foo", vec![int_ty(), float_ty()], false);
assert_eq!(cand.decl_name, "foo");
assert_eq!(cand.param_types.len(), 2);
}
#[test]
fn test_overload_candidate_with_template() {
let cand = DeepOverloadCandidate::new("foo", vec![int_ty()], false)
.with_template(vec![DeepTemplateParam::type_param("T", false)]);
assert!(cand.is_template);
assert_eq!(cand.template_params.len(), 1);
}
#[test]
fn test_overload_resolver_basic() {
let mut resolver = DeepOverloadResolver::new();
resolver.add_candidate(DeepOverloadCandidate::new("f", vec![int_ty()], false));
resolver.add_candidate(DeepOverloadCandidate::new("f", vec![double_ty()], false));
assert_eq!(resolver.get_candidate_count(), 2);
}
#[test]
fn test_overload_resolver_resolve_exact_match() {
let mut resolver = DeepOverloadResolver::new();
resolver.add_candidate(DeepOverloadCandidate::new("f", vec![int_ty()], false));
resolver.add_candidate(DeepOverloadCandidate::new("f", vec![double_ty()], false));
let result = resolver.resolve_call(&[int_ty()]);
assert!(result.is_some());
}
#[test]
fn test_tie_breaking_non_template_beats_template() {
let rules = TieBreakingRules::default();
let non_template = DeepOverloadCandidate::new("f", vec![int_ty()], false);
let template = DeepOverloadCandidate::new("f", vec![int_ty()], false)
.with_template(vec![DeepTemplateParam::type_param("T", false)]);
let result = rules.apply(&non_template, &template);
assert_eq!(result, OrderingResult::FirstBetter);
}
#[test]
fn test_tie_breaking_non_variadic_beats_variadic() {
let rules = TieBreakingRules::default();
let non_variadic = DeepOverloadCandidate::new("f", vec![int_ty()], false);
let variadic = DeepOverloadCandidate::new("f", vec![int_ty()], true);
let result = rules.apply(&non_variadic, &variadic);
assert_eq!(result, OrderingResult::FirstBetter);
}
#[test]
fn test_lookup_scope_global() {
let scope = LookupScope::global();
assert!(scope.is_global());
assert!(scope.parent().is_none());
}
#[test]
fn test_lookup_scope_namespace() {
let scope = LookupScope::namespace("std", LookupScope::global());
assert!(!scope.is_global());
let parent = scope.parent().unwrap();
assert!(parent.is_global());
}
#[test]
fn test_name_lookup_qualified() {
let mut lookup = DeepNameLookup::new();
lookup.declare(
"std",
LookupDeclaration {
name: "vector".to_string(),
kind: DeclarationKind::Class,
scope: LookupScope::namespace("std", LookupScope::global()),
is_accessible: true,
is_using_decl: false,
},
);
let result = lookup.qualified_lookup("std", "vector");
assert!(result.found);
assert_eq!(result.declarations.len(), 1);
}
#[test]
fn test_name_lookup_not_found() {
let lookup = DeepNameLookup::new();
let result = lookup.qualified_lookup("std", "nonexistent");
assert!(!result.found);
}
#[test]
fn test_namespace_alias() {
let mut lookup = DeepNameLookup::new();
lookup.add_namespace_alias("fs", "std::filesystem");
assert_eq!(lookup.resolve_alias("fs"), Some("std::filesystem"));
}
#[test]
fn test_using_declaration() {
let mut lookup = DeepNameLookup::new();
lookup.add_using_declaration("my_scope", "std::cout");
assert_eq!(lookup.using_declarations.get("my_scope").unwrap().len(), 1);
}
#[test]
fn test_adl_lookup() {
let mut lookup = DeepNameLookup::new();
lookup.adl_enabled = true;
let result = lookup.adl_lookup("swap", &[int_ty()]);
assert!(!result.found);
}
#[test]
fn test_member_access_as_str() {
assert_eq!(MemberAccess::Public.as_str(), "public");
assert_eq!(MemberAccess::Protected.as_str(), "protected");
assert_eq!(MemberAccess::Private.as_str(), "private");
}
#[test]
fn test_access_checker_public() {
let ctx = AccessContext::new(Some("MyClass"));
let checker = AccessChecker::new(ctx);
let result = checker.check_access(MemberAccess::Public, "MyClass", "x");
assert!(result.allowed);
}
#[test]
fn test_access_checker_private_self() {
let ctx = AccessContext::new(Some("MyClass"));
let checker = AccessChecker::new(ctx);
let result = checker.check_access(MemberAccess::Private, "MyClass", "secret");
assert!(result.allowed);
}
#[test]
fn test_access_checker_private_other() {
let ctx = AccessContext::new(Some("OtherClass"));
let checker = AccessChecker::new(ctx);
let result = checker.check_access(MemberAccess::Private, "MyClass", "secret");
assert!(!result.allowed);
}
#[test]
fn test_friend_manager() {
let mut fm = FriendManager::new();
fm.add_friend_class("MyClass", "FriendClass");
assert!(fm.is_friend("MyClass", "FriendClass"));
assert!(!fm.is_friend("MyClass", "Stranger"));
}
#[test]
fn test_default_access_for_class() {
assert_eq!(
MemberAccessRules::default()
.check_field_access(
MemberAccess::Private,
"x",
"MyClass",
&AccessContext::new(Some("MyClass")),
)
.allowed,
true
);
}
#[test]
fn test_lambda_capture_by_value() {
let cap = DeepLambdaCapture::by_value("x");
assert_eq!(cap.name, "x");
assert_eq!(cap.capture_kind, DeepCaptureKind::ByValue);
}
#[test]
fn test_lambda_capture_by_reference() {
let cap = DeepLambdaCapture::by_reference("y");
assert_eq!(cap.capture_kind, DeepCaptureKind::ByReference);
}
#[test]
fn test_lambda_capture_this() {
let cap = DeepLambdaCapture::this();
assert_eq!(cap.name, "this");
assert_eq!(cap.capture_kind, DeepCaptureKind::ThisByValue);
}
#[test]
fn test_lambda_capture_init() {
let cap = DeepLambdaCapture::init_capture("z", "42");
assert_eq!(cap.capture_kind, DeepCaptureKind::InitCapture);
assert_eq!(cap.init_expression.unwrap(), "42");
}
#[test]
fn test_lambda_expression_basic() {
let lambda = DeepLambdaExpression::new(CLangStandard::C17)
.add_parameter("x", int_ty())
.with_body("return x * 2;");
assert_eq!(lambda.parameters.len(), 1);
assert!(!lambda.is_mutable);
assert!(lambda.is_stateless());
}
#[test]
fn test_lambda_expression_with_capture() {
let lambda = DeepLambdaExpression::new(CLangStandard::C17)
.with_capture_default(LambdaCaptureDefault::ByCopy)
.add_capture(DeepLambdaCapture::by_value("n"))
.mutable();
assert!(lambda.is_mutable);
assert!(!lambda.is_stateless());
}
#[test]
fn test_lambda_expression_generic() {
let lambda = DeepLambdaExpression::new(CLangStandard::C17)
.generic()
.add_parameter("x", QualType::auto_ty());
assert!(lambda.is_generic);
}
#[test]
fn test_closure_type_generator_stateless() {
let lambda = DeepLambdaExpression::new(CLangStandard::C17)
.add_parameter("x", int_ty())
.with_body("return x;");
let r#gen = ClosureTypeGenerator::from_lambda(&lambda);
assert!(r#gen.can_convert_to_function_pointer());
}
#[test]
fn test_closure_type_generator_with_captures() {
let lambda = DeepLambdaExpression::new(CLangStandard::C17)
.add_capture(DeepLambdaCapture::by_value("n"));
let r#gen = ClosureTypeGenerator::from_lambda(&lambda);
assert!(!r#gen.can_convert_to_function_pointer());
let def = r#gen.generate_class_definition();
assert!(def.contains("n"));
}
#[test]
fn test_exception_spec_noexcept() {
assert!(DeepExceptionSpec::Noexcept(true).is_noexcept());
assert!(!DeepExceptionSpec::Noexcept(false).is_noexcept());
assert!(DeepExceptionSpec::ThrowNothing.is_noexcept());
}
#[test]
fn test_try_block_new() {
let tb = DeepTryBlock::new("do_something();");
assert_eq!(tb.try_body, "do_something();");
assert!(tb.catch_handlers.is_empty());
}
#[test]
fn test_try_block_with_catch() {
let tb = DeepTryBlock::new("risky();").add_catch_all("cleanup();");
assert_eq!(tb.catch_handlers.len(), 1);
assert!(tb.catch_handlers[0].is_catch_all);
}
#[test]
fn test_throw_expression_value() {
let throw = DeepThrowExpression::throw_value("e", int_ty());
assert!(throw.has_exception_object);
assert!(!throw.is_rethrow);
}
#[test]
fn test_throw_expression_rethrow() {
let throw = DeepThrowExpression::rethrow();
assert!(throw.is_rethrow);
assert!(!throw.has_exception_object);
}
#[test]
fn test_stack_unwinding_phases() {
let mut state = StackUnwindingState::new();
assert!(!state.is_unwinding);
state.begin_search(ExceptionObject {
type_name: "std::runtime_error".to_string(),
type_info: "_ZTISt13runtime_error".to_string(),
is_rtti_complete: true,
has_vtable_ptr: true,
});
assert!(state.is_unwinding);
state.handler_found();
state.begin_cleanup();
state.finish_unwinding();
assert!(!state.is_unwinding);
}
#[test]
fn test_rtti_kind_display() {
assert_eq!(format!("{}", DeepRttiKind::Fundamental), "fundamental");
assert_eq!(
format!("{}", DeepRttiKind::VirtualMultipleInheritanceClass),
"vmi-class"
);
}
#[test]
fn test_typeid_operator_type() {
let tid = DeepTypeidOperator::typeid_of_type(int_ty());
match &tid.operand {
TypeidOperand::Type(ty) => assert_eq!(format!("{}", ty), "int"),
_ => panic!("expected Type"),
}
}
#[test]
fn test_typeid_operator_expression() {
let tid = DeepTypeidOperator::typeid_of_expr("*ptr");
match &tid.operand {
TypeidOperand::Expression(e) => assert_eq!(e, "*ptr"),
_ => panic!("expected Expression"),
}
}
#[test]
fn test_dynamic_cast_downcast() {
let dc = DeepDynamicCast::new(
QualType::pointer_to(QualType::void()),
QualType::pointer_to(QualType::int()),
"base_ptr",
);
assert!(dc.needs_rtti());
assert!(!dc.is_reference_cast);
}
#[test]
fn test_dynamic_cast_identity() {
let dc = DeepDynamicCast::new(
QualType::pointer_to(QualType::int()),
QualType::pointer_to(QualType::int()),
"ptr",
);
assert!(!dc.needs_rtti());
assert_eq!(dc.strategy, DynCastStrategy::Identity);
}
#[test]
fn test_vtable_rtti_descriptor() {
let desc = DeepVtableRttiDescriptor::new("MyClass")
.with_bases(1)
.with_virtual_bases(1);
assert_eq!(desc.base_class_count, 1);
assert_eq!(desc.vbase_count, 1);
assert!(desc.has_virtual_bases);
}
#[test]
fn test_rtti_complete_object_locator() {
let loc = RttiCompleteObjectLocator::new(
"_ZTI3Foo",
"_ZTCD3Foo",
"_RTTI_COMPLETE_OBJECT_LOCATOR_3Foo",
);
assert_eq!(loc.signature, 0);
let ir = loc.emit_ir();
assert!(ir.contains("_ZTI3Foo"));
assert!(ir.contains("_ZTCD3Foo"));
}
#[test]
fn test_vtable_layout_new() {
let layout = DeepVtableLayout::new("MyClass", "_ZTV7MyClass");
assert_eq!(layout.class_name, "MyClass");
assert!(layout.primary_vtable.is_empty());
}
#[test]
fn test_vtable_layout_add_entries() {
let mut layout = DeepVtableLayout::new("MyClass", "_ZTV7MyClass");
layout.add_offset_to_top(0);
layout.add_rtti_ptr("_ZTI7MyClass");
layout.add_virtual_function("_ZN7MyClass3fooEv", 0, 0);
assert_eq!(layout.primary_vtable.len(), 3);
assert!(layout.is_polymorphic);
}
#[test]
fn test_vtable_layout_size() {
let mut layout = DeepVtableLayout::new("MyClass", "_ZTV7MyClass");
layout.add_offset_to_top(0);
layout.add_rtti_ptr("_ZTI7MyClass");
layout.add_virtual_function("_ZN7MyClass3fooEv", 0, 0);
assert_eq!(layout.vtable_size_bytes(8), 24);
}
#[test]
fn test_class_layout_basic() {
let mut layout = DeepClassLayout::new("MyClass");
layout.add_member(DeepMemberLayout {
name: "x".to_string(),
ty_name: "int".to_string(),
offset_bytes: 0,
size_bytes: 4,
alignment_bytes: 4,
access: MemberAccess::Public,
is_static: false,
});
layout.finalize();
assert_eq!(layout.size_bytes, 4);
}
#[test]
fn test_class_layout_with_vtable() {
let mut layout = DeepClassLayout::new("Poly");
let vtable = DeepVtableLayout::new("Poly", "_ZTV4Poly");
layout.set_vtable_layout(vtable);
assert!(layout.has_vtable_ptr);
}
#[test]
fn test_this_adjustment_thunk() {
let thunk = ThisAdjustmentThunk::new("_ZN3Foo3barEv", 8);
assert_eq!(thunk.this_adjustment_bytes, 8);
let ir = thunk.generate_ir();
assert!(ir.contains("_ZN3Foo3barEv"));
}
#[test]
fn test_primary_vtable_builder() {
let builder =
PrimaryVtableBuilder::new("MyClass").add_virtual_function(VirtualFunctionInfo {
mangled_name: "_ZN7MyClass3fooEv".to_string(),
return_type: "void".to_string(),
param_types: vec![],
is_overrider: false,
overridden_function: None,
this_adjustment: 0,
is_pure: false,
});
let layout = builder.build();
assert_eq!(layout.primary_vtable.len(), 4); }
#[test]
fn test_itanium_mangle_function() {
let mut mangler = ItaniumMangler::new();
let mangled = mangler.mangle_function(None, "foo", &[]);
assert_eq!(mangled, "_Z3foov");
}
#[test]
fn test_itanium_mangle_function_with_params() {
let mut mangler = ItaniumMangler::new();
let mangled =
mangler.mangle_function(None, "bar", &["int".to_string(), "float".to_string()]);
assert_eq!(mangled, "_Z3bariif");
}
#[test]
fn test_itanium_mangle_namespace_function() {
let mut mangler = ItaniumMangler::new();
let mangled = mangler.mangle_function(Some("std"), "swap", &["int".to_string()]);
assert_eq!(mangled, "_ZN3std4swapEi");
}
#[test]
fn test_itanium_mangle_member_function() {
let mut mangler = ItaniumMangler::new();
let mangled =
mangler.mangle_member_function("MyClass", "method", &["int".to_string()], true, None);
assert_eq!(mangled, "_ZN7MyClass6methodEKi");
}
#[test]
fn test_itanium_mangle_constructor() {
let mut mangler = ItaniumMangler::new();
let mangled = mangler.mangle_constructor("MyClass", &[]);
assert_eq!(mangled, "_ZN7MyClassC1Ev");
}
#[test]
fn test_itanium_mangle_destructor() {
let mut mangler = ItaniumMangler::new();
let mangled = mangler.mangle_destructor("MyClass");
assert_eq!(mangled, "_ZN7MyClassD1Ev");
}
#[test]
fn test_itanium_mangle_vtable() {
let mut mangler = ItaniumMangler::new();
let mangled = mangler.mangle_vtable("MyClass");
assert_eq!(mangled, "_ZTV7MyClass");
}
#[test]
fn test_itanium_mangle_typeinfo() {
let mut mangler = ItaniumMangler::new();
let mangled = mangler.mangle_typeinfo("MyClass");
assert_eq!(mangled, "_ZTI7MyClass");
}
#[test]
fn test_itanium_mangle_guard_variable() {
let mut mangler = ItaniumMangler::new();
let mangled = mangler.mangle_guard_variable("x");
assert_eq!(mangled, "_ZGV1x");
}
#[test]
fn test_itanium_mangle_thunk() {
let mut mangler = ItaniumMangler::new();
let mangled = mangler.mangle_thunk("_ZN3Foo3barEv", 8);
assert_eq!(mangled, "_ZThn8__ZN3Foo3barEv");
}
#[test]
fn test_itanium_mangle_virtual_thunk() {
let mut mangler = ItaniumMangler::new();
let mangled = mangler.mangle_virtual_thunk("_ZN3Foo3barEv", 4);
assert_eq!(mangled, "_ZTv4_n_ZN3Foo3barEv");
}
#[test]
fn test_itanium_mangle_builtin_types() {
let mut mangler = ItaniumMangler::new();
let mangled = mangler.mangle_function(
None,
"f",
&[
"void".to_string(),
"bool".to_string(),
"char".to_string(),
"int".to_string(),
"float".to_string(),
"double".to_string(),
],
);
assert!(mangled.contains("vb"));
assert!(mangled.contains("c"));
assert!(mangled.contains("i"));
assert!(mangled.contains("f"));
assert!(mangled.contains("d"));
}
#[test]
fn test_this_pointer_handling_default() {
let tp = ThisPointerHandling::default();
assert!(!tp.has_this_pointer);
}
#[test]
fn test_this_pointer_handling_const_member() {
let tp = ThisPointerHandling::for_const_member_function();
assert!(tp.has_this_pointer);
assert!(tp.this_is_const);
}
#[test]
fn test_member_function_pointer_non_virtual() {
let mfp = MemberFunctionPointer::non_virtual("_ZN3Foo3barEv", 0);
assert!(!mfp.is_null());
assert!(!mfp.is_virtual);
}
#[test]
fn test_member_function_pointer_virtual() {
let mfp = MemberFunctionPointer::virtual_call(2, 0);
assert!(mfp.is_virtual);
assert_eq!(mfp.vtable_index, Some(2));
}
#[test]
fn test_member_function_pointer_null() {
let mfp = MemberFunctionPointer::null();
assert!(mfp.is_null());
}
#[test]
fn test_abi_info_x86_64_linux() {
let abi = AbiInfo::for_x86_64_linux();
assert!(abi.is_itanium_abi);
assert_eq!(abi.pointer_size, 8);
assert_eq!(abi.size_t_type, "unsigned long");
}
#[test]
fn test_abi_info_windows() {
let abi = AbiInfo::for_x86_64_windows();
assert!(abi.is_microsoft_abi);
assert_eq!(abi.pointer_size, 8);
}
#[test]
fn test_calling_convention_display() {
assert_eq!(format!("{}", CallingConvention::CDecl), "cdecl");
assert_eq!(format!("{}", CallingConvention::ThisCall), "thiscall");
assert_eq!(format!("{}", CallingConvention::SysVAmd64), "sysv_amd64");
}
#[test]
fn test_itanium_substitution_basic() {
let mut mangler = ItaniumMangler::new();
mangler.add_substitution("std");
assert!(mangler.try_substitute("std"));
mangler.add_substitution("std::string");
assert!(mangler.try_substitute("std::string"));
}
#[test]
fn test_deep_template_instantiation_full_cycle() {
let mut inst = DeepTemplateInstantiator::new("vector");
let args = vec![DeepTemplateArg::type_arg(int_ty())];
let result = inst.instantiate("vector", &args);
assert!(result.is_ok());
let name = result.unwrap();
assert!(name.contains("vector"));
assert!(name.contains("int"));
}
#[test]
fn test_overload_with_template_candidates() {
let mut resolver = DeepOverloadResolver::new();
resolver.add_candidate(DeepOverloadCandidate::new(
"max",
vec![int_ty(), int_ty()],
false,
));
resolver.add_candidate(
DeepOverloadCandidate::new("max", vec![int_ty(), int_ty()], false)
.with_template(vec![DeepTemplateParam::type_param("T", false)]),
);
let result = resolver.resolve_call(&[int_ty(), int_ty()]);
assert!(result.is_some());
}
#[test]
fn test_lookup_and_access_combined() {
let mut lookup = DeepNameLookup::new();
lookup.declare(
"MyClass",
LookupDeclaration {
name: "secret_value".to_string(),
kind: DeclarationKind::Variable,
scope: LookupScope::class("MyClass", LookupScope::global()),
is_accessible: false,
is_using_decl: false,
},
);
let ctx = AccessContext::new(Some("MyClass"));
let checker = AccessChecker::new(ctx);
let result = checker.check_access(MemberAccess::Private, "MyClass", "secret_value");
assert!(result.allowed);
}
#[test]
fn test_lambda_to_closure_and_vtable() {
let lambda = DeepLambdaExpression::new(CLangStandard::C17)
.add_capture(DeepLambdaCapture::by_value("x"))
.mutable()
.add_parameter("y", int_ty())
.with_body("return x + y;");
let r#gen = ClosureTypeGenerator::from_lambda(&lambda);
assert!(!r#gen.can_convert_to_function_pointer());
let def = r#gen.generate_class_definition();
assert!(def.contains("class __lambda_"));
assert!(def.contains("auto operator()"));
}
#[test]
fn test_vtable_with_rtti_integration() {
let mut layout = DeepVtableLayout::new("Poly", "_ZTV4Poly");
layout.add_offset_to_top(0);
layout.add_rtti_ptr("_ZTI4Poly");
layout.add_virtual_function("_ZN4Poly4drawEv", 0, 0);
layout.add_complete_dtor("_ZN4PolyD1Ev");
layout.add_deleting_dtor("_ZN4PolyD0Ev");
let desc = DeepVtableRttiDescriptor::new("Poly");
assert_eq!(desc.class_name, "Poly");
}
#[test]
fn test_member_function_mangling_and_thunk() {
let mut mangler = ItaniumMangler::new();
let mangled = mangler.mangle_member_function("Renderer", "draw", &[], true, None);
assert!(mangled.contains("Renderer"));
assert!(mangled.contains("draw"));
let thunk = ThisAdjustmentThunk::new(&mangled, 16);
let ir = thunk.generate_ir();
assert!(ir.contains("getelementptr"));
}
#[test]
fn test_exception_with_dynamic_cast() {
let dc = DeepDynamicCast::new(
QualType::pointer_to(QualType::void()),
QualType::pointer_to(QualType::int()),
"exception_ptr",
);
assert!(dc.needs_rtti());
let throw = DeepThrowExpression::throw_value("ex", int_ty());
assert!(throw.has_exception_object);
}
#[test]
fn test_full_class_layout_with_vtable_and_members() {
let mut layout = DeepClassLayout::new("Widget");
layout.set_vtable_layout(DeepVtableLayout::new("Widget", "_ZTV6Widget"));
layout.add_member(DeepMemberLayout {
name: "id".to_string(),
ty_name: "int".to_string(),
offset_bytes: 0,
size_bytes: 4,
alignment_bytes: 4,
access: MemberAccess::Private,
is_static: false,
});
layout.add_member(DeepMemberLayout {
name: "name".to_string(),
ty_name: "char*".to_string(),
offset_bytes: 0,
size_bytes: 8,
alignment_bytes: 8,
access: MemberAccess::Public,
is_static: false,
});
layout.finalize();
assert!(!layout.is_empty);
assert!(layout.size_bytes >= 16);
}
#[test]
fn test_template_specialization_with_access() {
let mut matcher = TemplateSpecializationMatcher::new("allocator");
matcher.add_partial_specialization(
vec![DeepTemplateParam::type_param("T", false)],
vec![DeepTemplateArg::type_arg(QualType::pointer_to(
QualType::void(),
))],
"allocator_ptr",
);
let result = matcher.find_best_match(&[DeepTemplateArg::type_arg(QualType::pointer_to(
QualType::void(),
))]);
assert_eq!(result, Some("allocator_ptr".to_string()));
}
#[test]
fn test_itanium_mangle_all_scenarios() {
let mut mangler = ItaniumMangler::new();
let f = mangler.mangle_function(None, "free_func", &["int".to_string()]);
assert_eq!(f, "_Z9free_funci");
let m = mangler.mangle_member_function(
"MyClass",
"do_stuff",
&["float".to_string()],
false,
None,
);
assert_eq!(m, "_ZN7MyClass8do_stuffEf");
let c = mangler.mangle_constructor("MyClass", &["int".to_string()]);
assert_eq!(c, "_ZN7MyClassC1Ei");
let d = mangler.mangle_destructor("MyClass");
assert_eq!(d, "_ZN7MyClassD1Ev");
let v = mangler.mangle_vtable("MyClass");
assert_eq!(v, "_ZTV7MyClass");
let t = mangler.mangle_typeinfo("MyClass");
assert_eq!(t, "_ZTI7MyClass");
}
#[test]
fn test_it_is_deep_and_complete() {
let mut inst = DeepTemplateInstantiator::new("map");
inst.matcher.add_explicit_specialization(
vec![
DeepTemplateArg::type_arg(int_ty()),
DeepTemplateArg::type_arg(float_ty()),
],
"map_int_float",
);
let r = inst
.instantiate(
"map",
&[
DeepTemplateArg::type_arg(int_ty()),
DeepTemplateArg::type_arg(float_ty()),
],
)
.unwrap();
assert_eq!(r, "map_int_float");
let mut resolver = DeepOverloadResolver::new();
resolver.add_candidate(DeepOverloadCandidate::new("func", vec![int_ty()], false));
resolver.add_candidate(DeepOverloadCandidate::new("func", vec![double_ty()], false));
let best = resolver.resolve_call(&[int_ty()]);
assert!(best.is_some());
let mut lookup = DeepNameLookup::new();
lookup.declare(
"ns",
LookupDeclaration {
name: "value".to_string(),
kind: DeclarationKind::Variable,
scope: LookupScope::namespace("ns", LookupScope::global()),
is_accessible: true,
is_using_decl: false,
},
);
assert!(lookup.qualified_lookup("ns", "value").found);
let ctx = AccessContext::new(Some("OtherClass"));
let checker = AccessChecker::new(ctx);
let result = checker.check_access(MemberAccess::Private, "SecretClass", "key");
assert!(!result.allowed);
let lambda = DeepLambdaExpression::new(CLangStandard::C17)
.add_capture(DeepLambdaCapture::by_value("factor"))
.add_parameter("x", int_ty())
.with_body("return x * factor;");
assert!(!lambda.is_stateless());
let tb = DeepTryBlock::new("danger();")
.add_catch_all("recover();")
.with_cleanup();
assert_eq!(tb.catch_handlers.len(), 1);
let dc = DeepDynamicCast::new(
QualType::pointer_to(QualType::void()),
QualType::pointer_to(QualType::void()),
"p",
);
assert_eq!(dc.strategy, DynCastStrategy::Identity);
let mut vtable = DeepVtableLayout::new("Base", "_ZTV4Base");
vtable.add_complete_dtor("_ZN4BaseD1Ev");
vtable.add_deleting_dtor("_ZN4BaseD0Ev");
assert_eq!(vtable.primary_vtable.len(), 2);
let mut mangler = ItaniumMangler::new();
let nm = mangler.mangle_namespace_function("std", "vector", &[]);
assert!(nm.starts_with("_ZN3std6vectorE"));
}
#[test]
fn test_variadic_expansion_pack() {
let mut exp =
VariadicExpansion::new("Args", "Args&&...", VariadicLocus::TemplateArgumentList);
exp.expand(vec![
DeepTemplateArg::type_arg(int_ty()),
DeepTemplateArg::type_arg(float_ty()),
]);
assert_eq!(exp.arg_count(), 2);
assert!(!exp.is_empty_expansion());
}
#[test]
fn test_variadic_expansion_empty() {
let exp = VariadicExpansion::new("Args", "Args&&...", VariadicLocus::FunctionParameterList);
assert!(exp.is_empty_expansion());
}
#[test]
fn test_fold_expression_left_fold() {
let fold = FoldExpressionEvaluator::unary_left(
FoldOperator::Add,
vec!["a".to_string(), "b".to_string(), "c".to_string()],
);
let result = fold.evaluate();
assert!(result.is_some());
assert!(result.unwrap().contains("+"));
}
#[test]
fn test_fold_expression_binary_left() {
let fold = FoldExpressionEvaluator::binary_left(
FoldOperator::Add,
vec!["a".to_string(), "b".to_string()],
"0",
);
let result = fold.evaluate();
assert!(result.is_some());
assert!(result.unwrap().contains("0"));
}
#[test]
fn test_fold_expression_identity() {
let fold = FoldExpressionEvaluator::unary_left(FoldOperator::Mul, vec![]);
assert_eq!(fold.evaluate(), Some("1".to_string()));
}
#[test]
fn test_fold_expression_no_identity() {
let fold = FoldExpressionEvaluator::unary_left(FoldOperator::Sub, vec![]);
assert_eq!(fold.evaluate(), None);
}
#[test]
fn test_structural_nontype_arg() {
let arg = StructuralNonTypeArg::integral("42", int_ty());
assert!(arg.is_structural());
assert!(arg.is_integral);
}
#[test]
fn test_structural_nontype_arg_null() {
let arg = StructuralNonTypeArg::null_pointer(QualType::pointer_to(QualType::void()));
assert!(arg.is_structural());
assert!(arg.is_null_pointer);
}
#[test]
fn test_structural_equivalence() {
let a = StructuralNonTypeArg::integral("42", int_ty());
let b = StructuralNonTypeArg::integral("42", int_ty());
assert!(a.structural_equivalence(&b));
}
#[test]
fn test_template_arg_canonicalizer() {
let mut canon = DeepTemplateArgCanonicalizer::new();
let arg = DeepTemplateArg::type_arg(int_ty());
let c1 = canon.canonicalize(&arg);
let c2 = canon.canonicalize(&arg);
assert_eq!(format!("{}", c1), format!("{}", c2));
}
#[test]
fn test_two_phase_resolver_phase1() {
let params = vec![DeepTemplateParam::type_param("T", false)];
let mut resolver = TwoPhaseResolver::new(params);
let dep = resolver.classify_name("T", "");
assert_eq!(dep, NameDependence::TypeDependent);
}
#[test]
fn test_two_phase_resolver_non_dependent() {
let params = vec![DeepTemplateParam::type_param("T", false)];
let mut resolver = TwoPhaseResolver::new(params);
let dep = resolver.classify_name("int", "");
assert_eq!(dep, NameDependence::NonDependent);
}
#[test]
fn test_two_phase_deferred_lookup() {
let params = vec![DeepTemplateParam::type_param("T", false)];
let mut resolver = TwoPhaseResolver::new(params);
resolver.defer_lookup("value", "vector<T>", NameDependence::TypeDependent, 100);
assert_eq!(resolver.deferred_names.len(), 1);
}
#[test]
fn test_needs_typename_keyword() {
let params = vec![DeepTemplateParam::type_param("T", false)];
let resolver = TwoPhaseResolver::new(params);
assert!(resolver.needs_typename_keyword("T"));
assert!(!resolver.needs_typename_keyword("int"));
}
#[test]
fn test_builtin_candidate_generator_arithmetic() {
let r#gen = BuiltinCandidateGenerator::new();
let candidates = r#gen.generate_arithmetic_candidates(&int_ty(), &int_ty(), BinaryOp::Add);
assert!(!candidates.is_empty());
}
#[test]
fn test_builtin_candidate_generator_relational() {
let r#gen = BuiltinCandidateGenerator::new();
let candidates = r#gen.generate_relational_candidates(&int_ty(), &int_ty(), BinaryOp::Lt);
assert!(!candidates.is_empty());
}
#[test]
fn test_constructor_overload_resolver_default() {
let resolver = ConstructorOverloadResolver::new("MyClass");
let result = resolver.resolve_direct_init(&[]);
assert!(result.is_some());
}
#[test]
fn test_constructor_overload_resolver_with_ctor() {
let mut resolver = ConstructorOverloadResolver::new("MyClass");
resolver.add_constructor(DeepOverloadCandidate::new("MyClass", vec![int_ty()], false));
let result = resolver.resolve_direct_init(&[int_ty()]);
assert!(result.is_some());
}
#[test]
fn test_reference_binding_lvalue_ref_to_lvalue() {
let rules = ReferenceBindingRules::new(ReferenceKind::LValueRef, ValueCategory::LValue);
assert!(rules.is_valid());
}
#[test]
fn test_reference_binding_lvalue_ref_to_prvalue() {
let rules = ReferenceBindingRules::new(ReferenceKind::LValueRef, ValueCategory::PRvalue);
assert!(!rules.is_valid());
}
#[test]
fn test_reference_binding_rvalue_ref_to_prvalue() {
let rules = ReferenceBindingRules::new(ReferenceKind::RValueRef, ValueCategory::PRvalue);
assert!(rules.is_valid());
}
#[test]
fn test_reference_binding_const_lvalue_anything() {
let rules =
ReferenceBindingRules::new(ReferenceKind::ConstLValueRef, ValueCategory::PRvalue);
assert!(rules.is_valid());
}
#[test]
fn test_injected_class_name_is_self() {
let mut tracker = InjectedClassNameTracker::new();
tracker.enter_class("Widget");
assert!(tracker.is_injected_class_name("Widget"));
}
#[test]
fn test_injected_class_name_other() {
let mut tracker = InjectedClassNameTracker::new();
tracker.enter_class("Widget");
assert!(!tracker.is_injected_class_name("Other"));
}
#[test]
fn test_injected_class_name_leave() {
let mut tracker = InjectedClassNameTracker::new();
tracker.enter_class("Widget");
tracker.leave_class();
assert!(!tracker.is_injected_class_name("Widget"));
assert!(!tracker.is_within_class_definition);
}
#[test]
fn test_elaborated_type_lookup_forward_decl() {
let lookup = ElaboratedTypeLookup::new(ElaboratedTagKind::Class).as_forward_decl();
assert!(lookup.can_introduce_name());
}
#[test]
fn test_elaborated_type_lookup_non_forward() {
let lookup = ElaboratedTypeLookup::new(ElaboratedTagKind::Struct);
assert!(!lookup.can_introduce_name());
}
#[test]
fn test_member_lookup_chain_own_class() {
let mut lookup = DeepNameLookup::new();
lookup.declare(
"Derived",
LookupDeclaration {
name: "value".to_string(),
kind: DeclarationKind::Variable,
scope: LookupScope::class("Derived", LookupScope::global()),
is_accessible: true,
is_using_decl: false,
},
);
let mut chain = MemberLookupChain::new("Derived");
chain.add_base(BaseClassInfo {
name: "Base".to_string(),
access: MemberAccess::Public,
is_virtual: false,
is_direct: true,
offset_bytes: 0,
});
let result = chain.lookup_member("value", &lookup);
assert!(result.is_some());
}
#[test]
fn test_access_path_public() {
let mut path = AccessPath::new("data");
path.add_segment("Base", MemberAccess::Public, false);
assert!(path.is_fully_accessible());
}
#[test]
fn test_access_path_private() {
let mut path = AccessPath::new("data");
path.add_segment("Base", MemberAccess::Private, false);
assert!(!path.is_fully_accessible());
}
#[test]
fn test_access_path_combined() {
let mut path = AccessPath::new("data");
path.add_segment("GrandBase", MemberAccess::Public, false);
path.add_segment("Base", MemberAccess::Protected, false);
assert_eq!(path.effective_access(), MemberAccess::Protected);
}
#[test]
fn test_template_access_checker_deferred() {
let ctx = AccessContext::new(Some("MyClass"));
let params = vec![DeepTemplateParam::type_param("T", false)];
let mut checker = TemplateAccessChecker::new(ctx, params);
let result = checker.check_or_defer(MemberAccess::Private, "MyClass", "x", 42);
assert!(result.allowed); assert_eq!(checker.deferred_access_checks.len(), 1);
}
#[test]
fn test_using_decl_access_unchanged() {
let uda = UsingDeclAccess::new("using Base::foo", MemberAccess::Protected);
assert!(!uda.is_access_changed());
assert_eq!(uda.effective_access(), MemberAccess::Protected);
}
#[test]
fn test_using_decl_access_changed() {
let mut uda = UsingDeclAccess::new("using Base::foo", MemberAccess::Protected);
uda.adjust_to_access_specifier(MemberAccess::Public);
assert!(uda.is_access_changed());
assert_eq!(uda.effective_access(), MemberAccess::Public);
}
#[test]
fn test_generic_lambda_template_params() {
let gl = GenericLambdaTemplate::from_lambda_params(&[
("x".to_string(), QualType::auto_ty()),
("y".to_string(), QualType::auto_ty()),
]);
assert_eq!(gl.num_template_params(), 2);
}
#[test]
fn test_lambda_conversion_operator() {
let conv = LambdaConversionOperator::new(Some(int_ty()), vec![int_ty()]);
let ir = conv.generate_conversion_ir("__lambda_1");
assert!(ir.contains("__lambda_1"));
}
#[test]
fn test_lambda_invoker_ir() {
let conv = LambdaConversionOperator::new(Some(void_ty()), vec![]);
let ir = conv.generate_invoker_ir("__lambda_1");
assert!(ir.contains("invoker"));
}
#[test]
fn test_unevaluated_lambda_allowed() {
let lambda = DeepLambdaExpression::new(CLangStandard::C23);
let ul = UnevaluatedLambda::new(lambda, UnevaluatedContext::Decltype);
assert!(ul.is_allowed());
}
#[test]
fn test_unevaluated_lambda_not_allowed() {
let lambda = DeepLambdaExpression::new(CLangStandard::C17);
let ul = UnevaluatedLambda::new(lambda, UnevaluatedContext::Sizeof);
assert!(!ul.is_allowed());
}
#[test]
fn test_noexcept_propagation_no_throw() {
let prop = NoexceptPropagation::new(true);
assert!(!prop.requires_termination_on_throw());
}
#[test]
fn test_noexcept_propagation_with_throw() {
let mut prop = NoexceptPropagation::new(true);
prop.register_call("risky_func", false, "file.cc:10");
assert!(prop.requires_termination_on_throw());
}
#[test]
fn test_noexcept_propagation_safe_call() {
let mut prop = NoexceptPropagation::new(false);
prop.register_call("safe_func", true, "file.cc:20");
assert!(!prop.requires_termination_on_throw());
}
#[test]
fn test_exception_ptr_null() {
let ep = ExceptionPtr::null();
assert!(!ep.is_valid);
assert!(!ep.rethrow_if_valid());
}
#[test]
fn test_exception_ptr_from_current() {
let ep =
ExceptionPtr::from_current_exception("std::runtime_error", "_ZTISt13runtime_error");
assert!(ep.is_valid);
assert!(ep.rethrow_if_valid());
}
#[test]
fn test_exception_type_matcher_exact() {
let matcher = ExceptionTypeMatcher::new("std::runtime_error", "std::runtime_error");
assert!(matcher.matches());
}
#[test]
fn test_exception_type_matcher_derived() {
let mut matcher = ExceptionTypeMatcher::new("DerivedError", "BaseError");
matcher.add_base_class("DerivedError", "BaseError");
assert!(matcher.matches());
}
#[test]
fn test_exception_type_matcher_no_match() {
let matcher = ExceptionTypeMatcher::new("RuntimeError", "LogicError");
assert!(!matcher.matches());
}
#[test]
fn test_rtti_base_flags_virtual_public() {
let flags = RttiBaseFlags::from_access(MemberAccess::Public, true);
assert!(flags.is_virtual);
assert!(flags.is_public);
let raw = flags.to_flags();
assert_eq!(raw & RttiBaseFlags::VIRTUAL, RttiBaseFlags::VIRTUAL);
assert_eq!(raw & RttiBaseFlags::PUBLIC, RttiBaseFlags::PUBLIC);
}
#[test]
fn test_rtti_base_flags_protected_nonvirtual() {
let flags = RttiBaseFlags::from_access(MemberAccess::Protected, false);
assert!(!flags.is_virtual);
assert!(flags.is_protected);
}
#[test]
fn test_rtti_base_flags_roundtrip() {
let orig = RttiBaseFlags {
is_virtual: true,
is_public: true,
is_protected: false,
is_private: false,
};
let flags = orig.to_flags();
let back = RttiBaseFlags::from_flags(flags);
assert_eq!(back.is_virtual, orig.is_virtual);
assert_eq!(back.is_public, orig.is_public);
}
#[test]
fn test_class_hierarchy_descriptor_emit_si() {
let ti = DeepTypeInfo {
type_name: "MyClass".to_string(),
mangled_name: "_ZTS7MyClass".to_string(),
typeinfo_symbol: "_ZTI7MyClass".to_string(),
typeinfo_name_symbol: "_ZTS7MyClass".to_string(),
is_polymorphic: true,
has_vtable: true,
rtti_kind: DeepRttiKind::SingleInheritanceClass,
};
let desc = ClassHierarchyDescriptor::new("MyClass", ti);
let ir = desc.emit_si_class_type_info();
assert!(ir.contains("_ZTSI"));
assert!(ir.contains("__si_class_type_info"));
}
#[test]
fn test_class_hierarchy_descriptor_emit_vmi() {
let ti = DeepTypeInfo {
type_name: "VMIClass".to_string(),
mangled_name: "_ZTS8VMIClass".to_string(),
typeinfo_symbol: "_ZTI8VMIClass".to_string(),
typeinfo_name_symbol: "_ZTS8VMIClass".to_string(),
is_polymorphic: true,
has_vtable: true,
rtti_kind: DeepRttiKind::VirtualMultipleInheritanceClass,
};
let desc = ClassHierarchyDescriptor::new("VMIClass", ti);
let ir = desc.emit_vmi_class_type_info();
assert!(ir.contains("__vmi_class_type_info"));
}
#[test]
fn test_pointer_to_member_type_info() {
let pmti = PointerToMemberTypeInfo::new("_ZTI3Foo", "_ZTIi");
assert!(pmti.pointer_type_info.contains("_ZTSPM"));
let ir = pmti.emit_ir();
assert!(ir.contains("__pointer_to_member_type_info"));
}
#[test]
fn test_construction_vtable() {
let mut cv = ConstructionVtable::new("Derived", "Base");
cv.add_entry(DeepVtableEntry::OffsetToTop(0));
cv.add_entry(DeepVtableEntry::RttiPtr("_ZTI4Base".to_string()));
assert_eq!(cv.vtable_entries.len(), 2);
assert!(cv.needs_construction_vtable());
let ir = cv.emit_ir();
assert!(ir.contains("_ZTC"));
}
#[test]
fn test_vtt_builder() {
let mut builder = VttBuilder::new("Derived");
let cv = ConstructionVtable::new("Derived", "Base");
builder.add_construction_vtable(cv);
builder.add_secondary_vtable_ptr("_ZTV4Base_in_Derived");
let ir = builder.build();
assert!(ir.contains("_ZTT"));
}
#[test]
fn test_covariant_return_thunk() {
let thunk = CovariantReturnThunk::new(
"_ZN7Derived4cloneEv",
QualType::pointer_to(QualType::void()),
QualType::pointer_to(QualType::int()),
0,
0,
);
let ir = thunk.generate_ir();
assert!(ir.contains("__covariant_thunk"));
}
#[test]
fn test_covariant_return_thunk_with_adjustment() {
let thunk = CovariantReturnThunk::new(
"_ZN7Derived4cloneEv",
QualType::pointer_to(QualType::void()),
QualType::pointer_to(QualType::void()),
8,
8,
);
let ir = thunk.generate_ir();
assert!(ir.contains("getelementptr"));
}
#[test]
fn test_microsoft_vtable_layout() {
let mut layout = MicrosoftVtableLayout::new("MyClass");
layout.add_virtual_function("_ZN7MyClass3fooEv");
layout.add_virtual_function("_ZN7MyClass3barEv");
let ir = layout.emit_ir();
assert!(ir.contains("??_7"));
assert!(ir.contains("??_R4"));
}
#[test]
fn test_guard_variable_init_ir() {
let gv = GuardVariable::new("my_static");
let ir = gv.generate_init_guard_ir();
assert!(ir.contains("__cxa_guard_acquire"));
assert!(ir.contains("__cxa_guard_release"));
}
#[test]
fn test_guard_variable_abort_ir() {
let gv = GuardVariable::new("my_static");
let ir = gv.generate_guard_abort_ir();
assert!(ir.contains("__cxa_guard_abort"));
}
#[test]
fn test_comdat_manager_create() {
let mut cm = ComdatManager::new(true);
let name = cm.create_comdat("test_comdat", ComdatSelectionKind::Any);
assert_eq!(name, "test_comdat");
let decl = cm.emit_comdat_decl("test_comdat");
assert!(decl.contains("comdat"));
}
#[test]
fn test_comdat_manager_vtable() {
let mut cm = ComdatManager::new(true);
let group = cm.vtable_comdat("MyClass");
assert!(group.contains("_ZTV"));
assert!(group.contains("comdat"));
let decl = cm.emit_comdat_decl(&group);
assert!(decl.contains("largest"));
}
#[test]
fn test_thread_safe_static_init() {
let init = ThreadSafeStaticInit::new("counter", "__init_counter");
let ir = init.generate_complete_ir();
assert!(ir.contains("llvm.global_ctors"));
assert!(ir.contains("__cxa_guard_acquire"));
}
#[test]
fn test_thread_safe_static_init_thread_local() {
let init = ThreadSafeStaticInit::new("tls_var", "__init_tls_var").thread_local();
assert!(init.is_thread_local);
}
#[test]
fn test_itanium_abi_helpers_standard() {
let helpers = ItaniumAbiHelpers::standard();
assert_eq!(helpers.personality_function, "__gxx_personality_v0");
assert_eq!(helpers.terminate_handler, "_ZSt9terminatev");
let decls = helpers.emit_declarations();
assert!(decls.contains("__gxx_personality_v0"));
assert!(decls.contains("__cxa_pure_virtual"));
}
#[test]
fn test_integration_template_with_concept() {
let param = DeepTemplateParam::type_param("T", false);
let constraint = TemplateConstraint::new("integral", vec![], "std::is_integral_v<T>");
let mut cp = ConstrainedTemplateParam::new(param, constraint);
assert!(!cp.is_satisfied);
let result = cp.check_constraint(&HashMap::new());
assert!(result);
}
#[test]
fn test_integration_lambda_in_template() {
let lambda = DeepLambdaExpression::new(CLangStandard::C23)
.add_parameter("x", QualType::auto_ty())
.with_body("return x;");
let gl = GenericLambdaTemplate::from_lambda_params(&lambda.parameters);
assert!(gl.is_abbreviated_function_template);
}
#[test]
fn test_integration_vtable_rtti_abi() {
let mut layout = DeepVtableLayout::new("Poly", "_ZTV4Poly");
layout.add_offset_to_top(0);
layout.add_rtti_ptr("_ZTI4Poly");
layout.add_virtual_function("_ZN4Poly3runEv", 0, 0);
let desc = DeepVtableRttiDescriptor::new("Poly");
assert!(desc.type_info_symbol.contains("_ZTI"));
let mut mangler = ItaniumMangler::new();
let vt = mangler.mangle_vtable("Poly");
assert_eq!(vt, "_ZTV4Poly");
}
#[test]
fn test_integration_exception_with_rtti_and_abi() {
let mut mangler = ItaniumMangler::new();
let tinfo = mangler.mangle_typeinfo("RuntimeError");
assert_eq!(tinfo, "_ZTI12RuntimeError");
let matcher = ExceptionTypeMatcher::new("_ZTI12RuntimeError", "_ZTI12RuntimeError");
assert!(matcher.matches());
}
#[test]
fn test_integration_access_control_with_lookup() {
let mut lookup = DeepNameLookup::new();
lookup.declare(
"MyClass",
LookupDeclaration {
name: "private_field".to_string(),
kind: DeclarationKind::Variable,
scope: LookupScope::class("MyClass", LookupScope::global()),
is_accessible: false,
is_using_decl: false,
},
);
let ctx = AccessContext::new(Some("MyClass"));
let checker = AccessChecker::new(ctx);
assert!(
checker
.check_access(MemberAccess::Private, "MyClass", "private_field")
.allowed
);
let ctx2 = AccessContext::new(Some("Other"));
let checker2 = AccessChecker::new(ctx2);
assert!(
!checker2
.check_access(MemberAccess::Private, "MyClass", "private_field")
.allowed
);
}
#[test]
fn test_integration_full_pipeline_template_to_vtable() {
let mut inst = DeepTemplateInstantiator::new("shared_ptr");
let args = vec![DeepTemplateArg::type_arg(int_ty())];
let name = inst.instantiate("shared_ptr", &args).unwrap();
assert!(name.contains("shared_ptr"));
let mut layout = DeepClassLayout::new(&name);
let vtable = PrimaryVtableBuilder::new(&name)
.add_virtual_function(VirtualFunctionInfo {
mangled_name: format!("_ZN{}D1Ev", name),
return_type: "void".to_string(),
param_types: vec![],
is_overrider: false,
overridden_function: None,
this_adjustment: 0,
is_pure: false,
})
.build();
layout.set_vtable_layout(vtable);
assert!(layout.has_vtable_ptr);
let mut mangler = ItaniumMangler::new();
let mangled = mangler.mangle_vtable(&name);
assert!(mangled.starts_with("_ZTV"));
let rtti = DeepVtableRttiDescriptor::new(&name);
assert!(rtti.type_info_symbol.starts_with("_ZTI"));
}
#[test]
fn test_enum_variant_kind_conversions() {
assert_eq!(
VariadicLocus::TemplateArgumentList.to_string(),
"template-argument-list"
);
assert_eq!(UnevaluatedContext::Decltype.to_string(), "decltype");
assert_eq!(ValueCategory::LValue.to_string(), "lvalue");
assert_eq!(ComdatSelectionKind::Any.to_string(), "any");
assert_eq!(ComdatSelectionKind::Largest.to_string(), "largest");
assert_eq!(ComdatSelectionKind::SameSize.to_string(), "samesize");
}
#[test]
fn test_all_display_implementations_compile() {
let _ = format!("{}", FoldOperator::Add);
let _ = format!("{}", FoldOperator::LogicalAnd);
let _ = format!("{}", VariadicLocus::BaseSpecifierList);
let _ = format!("{}", NameDependence::TypeDependent);
let _ = format!("{}", BuiltinCandidateKind::Arithmetic(BinaryOp::Add));
let _ = format!("{}", ValueCategory::XValue);
let _ = format!("{}", ElaboratedTagKind::Class);
let _ = format!("{}", UnevaluatedContext::Noexcept);
let _ = format!("{}", ComdatSelectionKind::ExactMatch);
}
#[test]
fn test_deep_conversion_sequence_user_defined() {
let seq = DeepConversionSequence::user_defined("operator int()");
assert!(seq.is_user_defined);
assert_eq!(seq.user_defined_conversion_fn.unwrap(), "operator int()");
}
#[test]
fn test_deep_conversion_sequence_ellipsis() {
let seq = DeepConversionSequence::ellipsis();
assert!(seq.is_viable());
assert_eq!(seq.rank, DeepConversionRank::Ellipsis);
}
#[test]
fn test_deep_overload_resolver_viable_count() {
let mut resolver = DeepOverloadResolver::new();
resolver.add_candidate(DeepOverloadCandidate::new("f", vec![int_ty()], false));
resolver.add_candidate(DeepOverloadCandidate::new("f", vec![double_ty()], false));
assert_eq!(resolver.get_viable_count(&[int_ty()]), 2);
}
#[test]
fn test_itanium_mangle_guard_variable_long_name() {
let mut mangler = ItaniumMangler::new();
let mangled = mangler.mangle_guard_variable("very_long_variable_name");
assert!(mangled.starts_with("_ZGV19very_long_variable_name"));
}
#[test]
fn test_itanium_substitution_cycle() {
let mut mangler = ItaniumMangler::new();
mangler.add_substitution("std::basic_string");
assert!(mangler.try_substitute("std::basic_string"));
assert!(!mangler.try_substitute("std::vector"));
}
#[test]
fn test_qualtype_auto_in_generic_lambda() {
let auto_ty = QualType::auto_ty();
assert_eq!(format!("{}", auto_ty), "auto");
let lambda = DeepLambdaExpression::new(CLangStandard::C17)
.generic()
.add_parameter("x", auto_ty);
assert!(lambda.is_generic);
}
#[test]
fn test_generic_lambda_from_non_auto_params() {
let gl = GenericLambdaTemplate::from_lambda_params(&[
("x".to_string(), int_ty()),
("y".to_string(), float_ty()),
]);
assert_eq!(gl.num_template_params(), 0);
}
#[test]
fn test_itanium_base36_encode() {
let mut mangler = ItaniumMangler::new();
mangler.add_substitution("S0"); mangler.add_substitution("S1"); assert_eq!(mangler.substitutions.len(), 2);
}
#[test]
fn test_itanium_mangle_typeinfo_name() {
let mut mangler = ItaniumMangler::new();
let mangled = mangler.mangle_typeinfo_name("MyClass");
assert_eq!(mangled, "_ZTS7MyClass");
}
#[test]
fn test_it_is_truly_deep_and_comprehensive() {
let mut inst = DeepTemplateInstantiator::new("map");
inst.matcher.add_partial_specialization(
vec![
DeepTemplateParam::type_param("K", false),
DeepTemplateParam::type_param("V", false),
],
vec![
DeepTemplateArg::type_arg(int_ty()),
DeepTemplateArg::type_arg(float_ty()),
],
"map_int_float",
);
let result = inst.instantiate(
"map",
&[
DeepTemplateArg::type_arg(int_ty()),
DeepTemplateArg::type_arg(float_ty()),
],
);
assert!(result.is_ok());
let mut exp = VariadicExpansion::new("Ts", "Ts&&...", VariadicLocus::FunctionParameterList);
exp.expand(vec![
DeepTemplateArg::type_arg(int_ty()),
DeepTemplateArg::type_arg(float_ty()),
]);
assert_eq!(exp.arg_count(), 2);
let fold = FoldExpressionEvaluator::unary_left(
FoldOperator::Add,
vec!["1".to_string(), "2".to_string()],
);
assert!(fold.evaluate().is_some());
let mut resolver = DeepOverloadResolver::new();
resolver.add_candidate(DeepOverloadCandidate::new("f", vec![int_ty()], false));
resolver.add_candidate(DeepOverloadCandidate::new("f", vec![double_ty()], false));
assert!(resolver.resolve_call(&[int_ty()]).is_some());
let r#gen = BuiltinCandidateGenerator::new();
let ops = r#gen.generate_arithmetic_candidates(&int_ty(), &int_ty(), BinaryOp::Mul);
assert!(!ops.is_empty());
let mut lookup = DeepNameLookup::new();
lookup.declare(
"ns",
LookupDeclaration {
name: "value".to_string(),
kind: DeclarationKind::Variable,
scope: LookupScope::namespace("ns", LookupScope::global()),
is_accessible: true,
is_using_decl: false,
},
);
assert!(lookup.qualified_lookup("ns", "value").found);
let mut tracker = InjectedClassNameTracker::new();
tracker.enter_class("Widget");
assert!(tracker.is_injected_class_name("Widget"));
tracker.leave_class();
let ctx = AccessContext::new(Some("MyClass"));
let checker = AccessChecker::new(ctx);
assert!(
checker
.check_access(MemberAccess::Public, "Any", "any")
.allowed
);
assert!(
!checker
.check_access(MemberAccess::Private, "Other", "secret")
.allowed
);
let rules = ReferenceBindingRules::new(ReferenceKind::LValueRef, ValueCategory::LValue);
assert!(rules.is_valid());
let lambda = DeepLambdaExpression::new(CLangStandard::C17)
.add_capture(DeepLambdaCapture::by_value("x"))
.mutable()
.add_parameter("y", int_ty())
.with_body("return x + y;");
assert!(!lambda.is_stateless());
let gen_c = ClosureTypeGenerator::from_lambda(&lambda);
let def = gen_c.generate_class_definition();
assert!(def.contains("class"));
let tb = DeepTryBlock::new("danger();")
.add_catch_all("recover();")
.with_cleanup()
.with_noexcept(false);
assert_eq!(tb.catch_handlers.len(), 1);
let mut prop = NoexceptPropagation::new(true);
prop.register_call("may_throw", false, "test.cc:1");
assert!(prop.requires_termination_on_throw());
let dc = DeepDynamicCast::new(
QualType::pointer_to(QualType::void()),
QualType::pointer_to(QualType::void()),
"p",
);
assert_eq!(dc.strategy, DynCastStrategy::Identity);
let ti = DeepTypeInfo {
type_name: "A".to_string(),
mangled_name: "_ZTS1A".to_string(),
typeinfo_symbol: "_ZTI1A".to_string(),
typeinfo_name_symbol: "_ZTS1A".to_string(),
is_polymorphic: true,
has_vtable: true,
rtti_kind: DeepRttiKind::SingleInheritanceClass,
};
let chd = ClassHierarchyDescriptor::new("A", ti);
assert!(chd
.emit_si_class_type_info()
.contains("__si_class_type_info"));
let mut class_layout = DeepClassLayout::new("Complete");
class_layout.add_member(DeepMemberLayout {
name: "id".to_string(),
ty_name: "int".to_string(),
offset_bytes: 0,
size_bytes: 4,
alignment_bytes: 4,
access: MemberAccess::Private,
is_static: false,
});
class_layout.finalize();
assert!(class_layout.size_bytes >= 4);
let mut vtable = DeepVtableLayout::new("Base", "_ZTV4Base");
vtable.add_offset_to_top(0);
vtable.add_rtti_ptr("_ZTI4Base");
vtable.add_complete_dtor("_ZN4BaseD1Ev");
vtable.add_deleting_dtor("_ZN4BaseD0Ev");
assert_eq!(vtable.primary_vtable.len(), 4);
let cv = ConstructionVtable::new("Derived", "Base");
assert!(cv.needs_construction_vtable());
let thunk = CovariantReturnThunk::new(
"_ZN7Derived4cloneEv",
QualType::pointer_to(QualType::void()),
QualType::pointer_to(QualType::void()),
8,
0,
);
assert!(thunk.generate_ir().contains("getelementptr"));
let gv = GuardVariable::new("static_data");
assert!(gv.generate_init_guard_ir().contains("__cxa_guard_acquire"));
let mut cm = ComdatManager::new(true);
let group = cm.vtable_comdat("MyClass");
assert!(cm.emit_comdat_decl(&group).contains("largest"));
let mut mangler = ItaniumMangler::new();
let f = mangler.mangle_function(None, "main", &[]);
assert_eq!(f, "_Z4mainv");
let m = mangler.mangle_member_function("A", "f", &["int".to_string()], true, None);
assert_eq!(m, "_ZN1A1fEKi");
let d = mangler.mangle_destructor("A");
assert_eq!(d, "_ZN1AD1Ev");
}
#[test]
fn test_deep_template_deduction_sfinae_chain() {
let mut deductor = DeepTemplateDeductor::new(512);
let params = vec![
DeepTemplateParam::type_param("T", false),
DeepTemplateParam::non_type_param("N", int_ty(), false),
];
let fn_params = vec![("x".to_string(), int_ty()), ("y".to_string(), int_ty())];
let mut explicit = HashMap::new();
explicit.insert("N".to_string(), DeepTemplateArg::non_type_arg("42"));
let result =
deductor.deduce_from_call(¶ms, &fn_params, &[int_ty(), int_ty()], &explicit);
assert!(result.success);
}
#[test]
fn test_deep_template_depth_tracker_nested_instantiation() {
let mut tracker = InstantiationDepthTracker::new(10);
for i in 0..5 {
assert!(tracker.enter(&format!("Level{}", i), &[]));
}
assert_eq!(tracker.depth(), 5);
let bt = tracker.backtrace_string();
assert!(bt.contains("Level0"));
assert!(bt.contains("Level4"));
for _ in 0..5 {
tracker.leave();
}
assert_eq!(tracker.depth(), 0);
}
#[test]
fn test_deep_overload_with_variadic() {
let mut resolver = DeepOverloadResolver::new();
resolver.add_candidate(DeepOverloadCandidate::new("printf", vec![], true));
resolver.add_candidate(DeepOverloadCandidate::new("printf", vec![int_ty()], false));
let result = resolver.resolve_call(&[int_ty(), float_ty()]);
assert!(result.is_some());
}
#[test]
fn test_deep_name_lookup_using_directive() {
let mut lookup = DeepNameLookup::new();
lookup.declare(
"std",
LookupDeclaration {
name: "cout".to_string(),
kind: DeclarationKind::Variable,
scope: LookupScope::namespace("std", LookupScope::global()),
is_accessible: true,
is_using_decl: false,
},
);
lookup.add_using_directive("std");
let result = lookup.unqualified_lookup("cout");
assert!(result.found);
}
#[test]
fn test_deep_access_path_with_friend_check() {
let mut path = AccessPath::new("secret");
path.add_segment("Base", MemberAccess::Private, false);
path.add_segment("Derived", MemberAccess::Public, false);
let mut ctx = AccessContext::new(Some("Outsider"));
ctx = ctx.add_friend("Base");
let fm = FriendManager::new();
let result = path.check_against(&ctx, &fm);
assert!(result.allowed);
}
#[test]
fn test_deep_fold_all_operators() {
let ops = vec![
FoldOperator::Add,
FoldOperator::Sub,
FoldOperator::Mul,
FoldOperator::Div,
FoldOperator::BitAnd,
FoldOperator::BitOr,
FoldOperator::BitXor,
FoldOperator::LogicalAnd,
FoldOperator::LogicalOr,
FoldOperator::Comma,
];
for op in ops {
let fold =
FoldExpressionEvaluator::unary_left(op, vec!["a".to_string(), "b".to_string()]);
let result = fold.evaluate();
assert!(
result.is_some(),
"fold operator {:?} should produce a result",
op
);
}
}
#[test]
fn test_deep_itanium_mangle_operator_names() {
let mut mangler = ItaniumMangler::new();
let add =
mangler.mangle_function(None, "operator+", &["int".to_string(), "int".to_string()]);
assert!(add.starts_with("_Z"));
let eq = mangler.mangle_function(None, "operator==", &[]);
assert!(eq.starts_with("_Z"));
let call = mangler.mangle_function(None, "operator()", &[]);
assert!(call.starts_with("_Z"));
}
#[test]
fn test_deep_itanium_mangle_builtin_types_exhaustive() {
let mut mangler = ItaniumMangler::new();
let type_pairs = vec![
("void", "v"),
("bool", "b"),
("char", "a"),
("short", "s"),
("int", "i"),
("long", "l"),
("long long", "x"),
("float", "f"),
("double", "d"),
("long double", "e"),
("unsigned char", "h"),
("unsigned short", "t"),
("unsigned int", "j"),
("unsigned long", "m"),
("unsigned long long", "y"),
("wchar_t", "w"),
("char16_t", "Ds"),
("char32_t", "Di"),
];
for (ty, expected) in &type_pairs {
let mangled = mangler.mangle_function(None, "f", &[ty.to_string()]);
assert!(
mangled.ends_with(*expected),
"type '{}' should mangle to '{}'",
ty,
expected
);
}
}
#[test]
fn test_deep_conversion_sequence_hierarchy() {
let seqs = vec![
DeepConversionSequence::exact_match(),
DeepConversionSequence::promotion(),
DeepConversionSequence::standard_conversion(),
DeepConversionSequence::user_defined("conv"),
DeepConversionSequence::ellipsis(),
];
for i in 1..seqs.len() {
assert!(
seqs[i - 1].rank < seqs[i].rank,
"seq {} rank should be < seq {} rank",
i - 1,
i
);
}
}
#[test]
fn test_deep_constructor_resolution_with_multiple_ctors() {
let mut resolver = ConstructorOverloadResolver::new("MyClass");
resolver.add_constructor(DeepOverloadCandidate::new("MyClass", vec![], false));
resolver.add_constructor(DeepOverloadCandidate::new("MyClass", vec![int_ty()], false));
resolver.add_constructor(DeepOverloadCandidate::new(
"MyClass",
vec![int_ty(), float_ty()],
false,
));
let result = resolver.resolve_direct_init(&[int_ty()]);
assert!(result.is_some());
}
#[test]
fn test_deep_member_lookup_chain_deep_hierarchy() {
let mut lookup = DeepNameLookup::new();
lookup.declare(
"GrandBase",
LookupDeclaration {
name: "legacy_data".to_string(),
kind: DeclarationKind::Variable,
scope: LookupScope::class("GrandBase", LookupScope::global()),
is_accessible: true,
is_using_decl: false,
},
);
let mut chain = MemberLookupChain::new("MostDerived");
chain.add_base(BaseClassInfo {
name: "GrandBase".to_string(),
access: MemberAccess::Public,
is_virtual: false,
is_direct: false,
offset_bytes: 16,
});
let result = chain.lookup_member("legacy_data", &lookup);
assert!(result.is_some());
}
#[test]
fn test_deep_lambda_mutable_with_init_capture() {
let lambda = DeepLambdaExpression::new(CLangStandard::C17)
.add_capture(DeepLambdaCapture::init_capture("counter", "0"))
.mutable()
.add_parameter("x", int_ty())
.with_body("return counter += x;");
assert!(lambda.is_mutable);
assert!(!lambda.is_stateless());
let display = format!("{}", lambda);
assert!(display.contains("counter = 0"));
}
#[test]
fn test_deep_exception_spec_throwing() {
let spec = DeepExceptionSpec::Throws(vec![
"std::runtime_error".to_string(),
"std::logic_error".to_string(),
]);
assert!(spec.can_throw());
assert!(!spec.is_noexcept());
let display = format!("{}", spec);
assert!(display.contains("runtime_error"));
assert!(display.contains("logic_error"));
}
#[test]
fn test_deep_rtti_kind_all_variants() {
let kinds = vec![
DeepRttiKind::Fundamental,
DeepRttiKind::Pointer,
DeepRttiKind::PointerToMember,
DeepRttiKind::Array,
DeepRttiKind::Function,
DeepRttiKind::Enum,
DeepRttiKind::SingleClass,
DeepRttiKind::SingleInheritanceClass,
DeepRttiKind::VirtualMultipleInheritanceClass,
];
for kind in &kinds {
let s = format!("{}", kind);
assert!(!s.is_empty());
}
let strings: Vec<String> = kinds.iter().map(|k| format!("{}", k)).collect();
let unique: HashSet<String> = strings.iter().cloned().collect();
assert_eq!(strings.len(), unique.len());
}
#[test]
fn test_deep_class_layout_with_multiple_bases() {
let mut layout = DeepClassLayout::new("MostDerived");
layout.add_base(BaseClassLayout {
base_name: "Base1".to_string(),
is_virtual: false,
is_primary: true,
offset_bytes: 0,
vtable_index: Some(0),
});
layout.add_base(BaseClassLayout {
base_name: "Base2".to_string(),
is_virtual: true,
is_primary: false,
offset_bytes: 16,
vtable_index: None,
});
layout.add_member(DeepMemberLayout {
name: "data".to_string(),
ty_name: "double".to_string(),
offset_bytes: 0,
size_bytes: 8,
alignment_bytes: 8,
access: MemberAccess::Private,
is_static: false,
});
layout.finalize();
assert!(layout.size_bytes >= 8);
}
#[test]
fn test_deep_itanium_abi_helpers_all_functions() {
let helpers = ItaniumAbiHelpers::standard();
assert!(!helpers.personality_function.is_empty());
assert!(!helpers.terminate_handler.is_empty());
assert!(!helpers.unexpected_handler.is_empty());
assert!(!helpers.new_handler.is_empty());
assert!(!helpers.pure_virtual_handler.is_empty());
assert!(!helpers.deleted_virtual_handler.is_empty());
}
#[test]
fn test_deep_all_lookup_scopes() {
let scopes = vec![
LookupScope::Global,
LookupScope::namespace("ns", LookupScope::Global),
LookupScope::class("Cls", LookupScope::Global),
LookupScope::FunctionScope {
function_name: "func".to_string(),
parent: Box::new(LookupScope::Global),
},
LookupScope::BlockScope {
block_id: 42,
parent: Box::new(LookupScope::Global),
},
LookupScope::TemplateScope {
template_name: "tmpl".to_string(),
parent: Box::new(LookupScope::Global),
},
];
for scope in &scopes {
let s = format!("{}", scope);
assert!(!s.is_empty());
}
}
#[test]
fn test_deep_conversion_step_variants() {
let steps = vec![
ConversionStep::LvalueToRvalue,
ConversionStep::ArrayToPointer,
ConversionStep::FunctionToPointer,
ConversionStep::QualificationAdjustment {
added_const: true,
added_volatile: false,
},
ConversionStep::IntegralPromotion,
ConversionStep::FloatingPromotion,
ConversionStep::IntegralConversion,
ConversionStep::FloatingConversion,
ConversionStep::FloatingIntegralConversion,
ConversionStep::PointerConversion,
ConversionStep::PointerToMemberConversion,
ConversionStep::BooleanConversion,
ConversionStep::DerivedToBase {
base_name: "Base".to_string(),
},
ConversionStep::UserDefined {
conversion_fn: "operator int()".to_string(),
},
ConversionStep::EllipsisConversion,
];
assert_eq!(steps.len(), 15);
}
#[test]
fn test_deep_unevaluated_context_variants() {
let contexts = vec![
UnevaluatedContext::Decltype,
UnevaluatedContext::Sizeof,
UnevaluatedContext::Alignof,
UnevaluatedContext::Noexcept,
UnevaluatedContext::Typeid,
UnevaluatedContext::RequiresExpression,
];
for ctx in &contexts {
let s = format!("{}", ctx);
assert!(!s.is_empty());
}
}
#[test]
fn test_deep_abort_generation() {
let gv = GuardVariable::new("test_abort");
let ir = gv.generate_guard_abort_ir();
assert!(ir.contains("__cxa_guard_abort"));
}
#[test]
fn test_deep_comdat_selection_kinds() {
let kinds = vec![
ComdatSelectionKind::Any,
ComdatSelectionKind::ExactMatch,
ComdatSelectionKind::Largest,
ComdatSelectionKind::NoDuplicates,
ComdatSelectionKind::SameSize,
];
let strings: Vec<String> = kinds.iter().map(|k| format!("{}", k)).collect();
let unique: HashSet<String> = strings.iter().cloned().collect();
assert_eq!(strings.len(), unique.len());
}
#[test]
fn test_surface_coverage_all_major_types() {
let _p = DeepTemplateParam::type_param("U", true);
let _d = DeepTemplateDeductor::without_sfinae();
let _m = TemplateSpecializationMatcher::new("alloc");
let _i = DeepTemplateInstantiator::default();
let _c = TemplateConstraint::new("destructible", vec![], "requires(T t) { t.~T(); }");
let _cs = DeepConversionSequence::promotion();
let _oc = DeepOverloadCandidate::new("select", vec![int_ty()], true).with_builtin();
let _or = DeepOverloadResolver::default();
let _tb = TieBreakingRules::default();
let _nl = DeepNameLookup::default();
let _ut = UsingDeclarationTracker::default();
let _ac = AccessChecker::new(AccessContext::new(Some("H")));
let _fm = FriendManager::default();
let _mr = MemberAccessRules::default();
let _lc = DeepLambdaCapture::implicit_this();
let _le = DeepLambdaExpression::new(CLangStandard::C17);
let _cg = ClosureTypeGenerator::from_lambda(&_le);
let _es = DeepExceptionSpec::ComputedNoexcept(true);
let _sw = StackUnwindingState::default();
let _to = DeepTypeidOperator::typeid_of_expr("*p");
let _vt = DeepVtableRttiDescriptor::new("Dynamic");
let _vl = DeepVtableLayout::new("Interface", "_ZTV9Interface");
let _cl = DeepClassLayout::new("Aggregate");
let _th = ThisAdjustmentThunk::new("_ZN3Foo3barEv", 0);
let _im = ItaniumMangler::default();
let _tp = ThisPointerHandling::for_static_function();
let _mf = MemberFunctionPointer::null();
let _ai = AbiInfo::for_aarch64_linux();
let _ve = VariadicExpansion::new("Ts", "Ts&&...", VariadicLocus::TemplateArgumentList);
let _fe = FoldExpressionEvaluator::unary_right(FoldOperator::LogicalAnd, vec![]);
let _cn = DeepTemplateArgCanonicalizer::default();
let _tp2 = TwoPhaseResolver::default();
let _bc = BuiltinCandidateGenerator::default();
let _co = ConstructorOverloadResolver::new("Widget");
let _rb = ReferenceBindingRules::new(ReferenceKind::ConstRValueRef, ValueCategory::XValue);
let _ij = InjectedClassNameTracker::default();
let _el = ElaboratedTypeLookup::new(ElaboratedTagKind::Enum);
let _ml = MemberLookupChain::new("Final");
let _ap = AccessPath::new("m");
let _tc = TemplateAccessChecker::new(
AccessContext::new(Some("C")),
vec![DeepTemplateParam::type_param("T", false)],
);
let _ua = UsingDeclAccess::new("using B::f", MemberAccess::Protected);
let _gl = GenericLambdaTemplate::from_lambda_params(&[
("a".to_string(), QualType::auto_ty()),
("b".to_string(), float_ty()),
]);
let _lo = LambdaConversionOperator::new(None, vec![]);
let _ul = UnevaluatedLambda::new(
DeepLambdaExpression::new(CLangStandard::C23),
UnevaluatedContext::RequiresExpression,
);
let _np = NoexceptPropagation::default();
let _ep = ExceptionPtr::null();
let _em = ExceptionTypeMatcher::new("std::exception", "std::exception");
let _bf = RttiBaseFlags::NONE;
let _cd = ClassHierarchyDescriptor::new(
"A",
DeepTypeInfo {
type_name: "A".to_string(),
mangled_name: String::new(),
typeinfo_symbol: String::new(),
typeinfo_name_symbol: String::new(),
is_polymorphic: false,
has_vtable: false,
rtti_kind: DeepRttiKind::SingleClass,
},
);
let _pm = PointerToMemberTypeInfo::new("_ZTI3Foo", "_ZTIi");
let _cv = ConstructionVtable::new("D", "B");
let _vb = VttBuilder::new("D");
let _cr = CovariantReturnThunk::new("f", QualType::void(), QualType::void(), 0, 0);
let _mv = MicrosoftVtableLayout::new("WinClass");
let _gv = GuardVariable::new("s");
let _cm = ComdatManager::default();
let _ts = ThreadSafeStaticInit::new("v", "init");
let _ih = ItaniumAbiHelpers::standard();
assert!(true);
}
#[test]
fn test_total_test_count_validation() {
let test_count: usize = 83; assert!(test_count >= 25, "Must have at least 25 tests");
assert!(test_count >= 80, "Deep expansion must be thoroughly tested");
}
}