use std::collections::{BTreeMap, BTreeSet, HashMap, VecDeque};
use std::fmt;
use crate::clang::{
BinaryOp, CLangStandard, CompoundStmt, Decl, Expr, FunctionDecl, Linkage, QualType, Stmt,
TranslationUnit, UnaryOp, VarDecl, X86DeepSema, X86LookupPhase, X86NameDependence,
X86ScopeKind, X86ScopeManager, X86TemplateSema,
};
pub const X86_MAX_TEMPLATE_DEPTH: usize = 512;
pub const X86_MAX_CONSTEXPR_STEPS: u64 = 1_048_576;
pub const X86_MAX_CONSTEXPR_DEPTH: usize = 512;
pub const X86_MAX_SFINAE_CONTEXTS: usize = 64;
pub const X86_CONCEPT_NORMALIZE_DEPTH: usize = 32;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum X86EnhancedScopeKind {
FunctionPrototype,
FunctionBody,
Block,
Namespace,
Class,
Struct,
Enum,
TemplateParam,
TemplateInstantiation,
TryBlock,
CatchBlock,
Lambda,
RequiresClause,
ConstexprEval,
EnumClass,
LinkageSpec,
Friend,
ExplicitSpecialization,
ExplicitInstantiation,
}
impl fmt::Display for X86EnhancedScopeKind {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::FunctionPrototype => write!(f, "function-prototype"),
Self::FunctionBody => write!(f, "function-body"),
Self::Block => write!(f, "block"),
Self::Namespace => write!(f, "namespace"),
Self::Class => write!(f, "class"),
Self::Struct => write!(f, "struct"),
Self::Enum => write!(f, "enum"),
Self::TemplateParam => write!(f, "template-param"),
Self::TemplateInstantiation => write!(f, "template-instantiation"),
Self::TryBlock => write!(f, "try-block"),
Self::CatchBlock => write!(f, "catch-block"),
Self::Lambda => write!(f, "lambda"),
Self::RequiresClause => write!(f, "requires-clause"),
Self::ConstexprEval => write!(f, "constexpr-eval"),
Self::EnumClass => write!(f, "enum-class"),
Self::LinkageSpec => write!(f, "linkage-spec"),
Self::Friend => write!(f, "friend"),
Self::ExplicitSpecialization => write!(f, "explicit-specialization"),
Self::ExplicitInstantiation => write!(f, "explicit-instantiation"),
}
}
}
#[derive(Debug, Clone)]
pub enum X86EnhancedScopeEntry {
Variable {
name: String,
ty: String,
is_extern: bool,
is_static: bool,
declared_line: usize,
declared_col: usize,
is_thread_local: bool,
is_constexpr: bool,
},
Function {
name: String,
ret_ty: String,
params: Vec<String>,
is_vararg: bool,
is_inline: bool,
linkage: String,
declared_line: usize,
declared_col: usize,
is_constexpr: bool,
is_noexcept: bool,
},
Typedef { name: String, underlying: String },
StructTag {
name: String,
is_union: bool,
is_final: bool,
},
EnumTag {
name: String,
is_scoped: bool,
underlying_type: Option<String>,
},
EnumConstant { name: String, value: i128 },
Label { name: String },
Namespace { name: String, is_inline: bool },
TemplateParam {
name: String,
kind: String,
is_pack: bool,
},
TemplateTypeParam {
name: String,
default_type: Option<String>,
},
UsingDecl {
name: String,
target: String,
is_typename: bool,
},
UsingDirective { namespace: String },
Concept { name: String, constraints: String },
}
impl X86EnhancedScopeEntry {
pub fn name(&self) -> &str {
match self {
Self::Variable { name, .. } => name,
Self::Function { name, .. } => name,
Self::Typedef { name, .. } => name,
Self::StructTag { name, .. } => name,
Self::EnumTag { name, .. } => name,
Self::EnumConstant { name, .. } => name,
Self::Label { name } => name,
Self::Namespace { name, .. } => name,
Self::TemplateParam { name, .. } => name,
Self::TemplateTypeParam { name, .. } => name,
Self::UsingDecl { name, .. } => name,
Self::UsingDirective { namespace } => namespace,
Self::Concept { name, .. } => name,
}
}
pub fn is_type(&self) -> bool {
matches!(
self,
Self::Typedef { .. }
| Self::StructTag { .. }
| Self::EnumTag { .. }
| Self::TemplateTypeParam { .. }
| Self::Concept { .. }
| Self::UsingDecl {
is_typename: true,
..
}
)
}
}
#[derive(Debug, Clone)]
pub struct X86EnhancedScope {
pub kind: X86EnhancedScopeKind,
pub entries: BTreeMap<String, X86EnhancedScopeEntry>,
pub labels: BTreeSet<String>,
pub has_return_stmt: bool,
pub is_closed: bool,
pub name: Option<String>,
pub parent_kind: Option<X86EnhancedScopeKind>,
}
impl X86EnhancedScope {
pub fn new(kind: X86EnhancedScopeKind) -> Self {
Self {
kind,
entries: BTreeMap::new(),
labels: BTreeSet::new(),
has_return_stmt: false,
is_closed: false,
name: None,
parent_kind: None,
}
}
pub fn with_name(kind: X86EnhancedScopeKind, name: &str) -> Self {
Self {
kind,
entries: BTreeMap::new(),
labels: BTreeSet::new(),
has_return_stmt: false,
is_closed: false,
name: Some(name.to_string()),
parent_kind: None,
}
}
pub fn insert(&mut self, entry: X86EnhancedScopeEntry) {
self.entries.insert(entry.name().to_string(), entry);
}
pub fn lookup(&self, name: &str) -> Option<&X86EnhancedScopeEntry> {
self.entries.get(name)
}
pub fn define_label(&mut self, name: &str) {
self.labels.insert(name.to_string());
}
pub fn has_label(&self, name: &str) -> bool {
self.labels.contains(name)
}
pub fn mark_returned(&mut self) {
self.has_return_stmt = true;
}
}
#[derive(Debug, Clone)]
pub struct X86EnhancedScopeManager {
pub scopes: Vec<X86EnhancedScope>,
pub namespaces: BTreeMap<String, Vec<X86EnhancedScopeEntry>>,
pub current_function: Option<String>,
pub current_return_type: Option<String>,
pub loop_depth: usize,
pub switch_depth: usize,
pub warn_shadow: bool,
pub enforce_odr: bool,
pub odr_functions: HashMap<String, Vec<(String, usize)>>,
pub odr_variables: HashMap<String, Vec<(String, usize)>>,
pub in_requires_clause: bool,
pub template_param_depths: Vec<usize>,
pub current_namespace: Vec<String>,
pub using_directives: Vec<String>,
}
impl X86EnhancedScopeManager {
pub fn new() -> Self {
Self {
scopes: Vec::new(),
namespaces: BTreeMap::new(),
current_function: None,
current_return_type: None,
loop_depth: 0,
switch_depth: 0,
warn_shadow: true,
enforce_odr: true,
odr_functions: HashMap::new(),
odr_variables: HashMap::new(),
in_requires_clause: false,
template_param_depths: Vec::new(),
current_namespace: Vec::new(),
using_directives: Vec::new(),
}
}
pub fn enter_scope(&mut self, kind: X86EnhancedScopeKind) {
let parent_kind = self.scopes.last().map(|s| s.kind);
let mut scope = X86EnhancedScope::new(kind);
scope.parent_kind = parent_kind;
self.scopes.push(scope);
}
pub fn enter_named_scope(&mut self, kind: X86EnhancedScopeKind, name: &str) {
let parent_kind = self.scopes.last().map(|s| s.kind);
let mut scope = X86EnhancedScope::with_name(kind, name);
scope.parent_kind = parent_kind;
self.scopes.push(scope);
}
pub fn leave_scope(&mut self) {
if let Some(scope) = self.scopes.pop() {
if !scope.is_closed {
}
}
}
pub fn current_scope(&self) -> Option<&X86EnhancedScope> {
self.scopes.last()
}
pub fn current_scope_mut(&mut self) -> Option<&mut X86EnhancedScope> {
self.scopes.last_mut()
}
pub fn scope_depth(&self) -> usize {
self.scopes.len()
}
pub fn at_file_scope(&self) -> bool {
self.scopes.len() <= 1
}
pub fn in_function(&self) -> bool {
self.current_function.is_some()
}
pub fn declare(&mut self, entry: X86EnhancedScopeEntry) -> Result<(), String> {
let name = entry.name().to_string();
if self.warn_shadow {
for scope in self.scopes.iter().rev().skip(1) {
if scope.lookup(&name).is_some() {
break;
}
}
}
if self.enforce_odr {
if let X86EnhancedScopeEntry::Function {
name: fname, ..
} = &entry
{
if self.scopes.len() == 1 {
let key = fname.clone();
let scope_info = format!("{:?}", self.current_scope().map(|s| s.kind));
let locations = self.odr_functions.entry(key.clone()).or_default();
locations.push((scope_info, 0));
if locations.len() > 1 {
let has_definition = locations.len() > 1;
if has_definition {
return Err(format!(
"ODR violation: function '{}' defined more than once",
key
));
}
}
}
}
}
if let Some(scope) = self.scopes.last_mut() {
scope.insert(entry);
}
Ok(())
}
pub fn lookup(&self, name: &str) -> Option<&X86EnhancedScopeEntry> {
for scope in self.scopes.iter().rev() {
if let entry @ Some(_) = scope.lookup(name) {
return entry;
}
}
None
}
pub fn lookup_in_current_scope(&self, name: &str) -> Option<&X86EnhancedScopeEntry> {
self.scopes.last().and_then(|s| s.lookup(name))
}
pub fn lookup_in_namespace(&self, ns: &str, name: &str) -> Option<&X86EnhancedScopeEntry> {
self.namespaces
.get(ns)
.and_then(|entries| entries.iter().find(|e| e.name() == name))
}
pub fn enter_loop(&mut self) {
self.loop_depth += 1;
}
pub fn leave_loop(&mut self) {
if self.loop_depth > 0 {
self.loop_depth -= 1;
}
}
pub fn in_loop(&self) -> bool {
self.loop_depth > 0
}
pub fn enter_switch(&mut self) {
self.switch_depth += 1;
}
pub fn leave_switch(&mut self) {
if self.switch_depth > 0 {
self.switch_depth -= 1;
}
}
pub fn in_switch(&self) -> bool {
self.switch_depth > 0
}
pub fn enter_namespace(&mut self, name: &str) {
self.current_namespace.push(name.to_string());
self.enter_named_scope(X86EnhancedScopeKind::Namespace, name);
}
pub fn leave_namespace(&mut self) {
self.current_namespace.pop();
self.leave_scope();
}
pub fn register_namespace(&mut self, name: &str) {
self.namespaces.entry(name.to_string()).or_default();
}
pub fn total_declarations(&self) -> usize {
self.scopes.iter().map(|s| s.entries.len()).sum()
}
pub fn dump_scopes(&self) -> String {
let mut out = String::new();
for (i, scope) in self.scopes.iter().enumerate() {
out.push_str(&format!(
"[{}] {:?} ({} entries, has_return={}, closed={})\n",
i,
scope.kind,
scope.entries.len(),
scope.has_return_stmt,
scope.is_closed
));
for (name, entry) in &scope.entries {
out.push_str(&format!(" {} -> {:?}\n", name, entry));
}
}
out
}
}
impl Default for X86EnhancedScopeManager {
fn default() -> Self {
Self::new()
}
}
#[derive(Debug, Clone)]
pub struct X86InstantiationFrame {
pub template_name: String,
pub arguments: Vec<String>,
pub source_file: Option<String>,
pub source_line: usize,
pub source_column: usize,
pub depth: usize,
pub kind: X86InstantiationKind,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum X86InstantiationKind {
Explicit,
Implicit,
DefaultArgument,
PartialSpecialization,
NonTypeDeduction,
MemberAccess,
Friend,
}
impl fmt::Display for X86InstantiationKind {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::Explicit => write!(f, "explicit"),
Self::Implicit => write!(f, "implicit"),
Self::DefaultArgument => write!(f, "default-argument"),
Self::PartialSpecialization => write!(f, "partial-specialization"),
Self::NonTypeDeduction => write!(f, "nontype-deduction"),
Self::MemberAccess => write!(f, "member-access"),
Self::Friend => write!(f, "friend"),
}
}
}
#[derive(Debug, Clone)]
pub struct X86DepthTracker {
pub current_depth: usize,
pub maximum_depth: usize,
pub backtrace: Vec<X86InstantiationFrame>,
pub total_instantiations: usize,
pub rejected_instantiations: usize,
}
impl X86DepthTracker {
pub fn new(maximum_depth: usize) -> Self {
Self {
current_depth: 0,
maximum_depth,
backtrace: Vec::new(),
total_instantiations: 0,
rejected_instantiations: 0,
}
}
pub fn enter(
&mut self,
template_name: &str,
arguments: Vec<String>,
line: usize,
col: usize,
kind: X86InstantiationKind,
) -> Result<usize, String> {
if self.current_depth >= self.maximum_depth {
self.rejected_instantiations += 1;
return Err(format!(
"template instantiation depth ({}) exceeds maximum ({}). Use -ftemplate-depth=N to increase.",
self.current_depth + 1,
self.maximum_depth
));
}
self.current_depth += 1;
self.total_instantiations += 1;
self.backtrace.push(X86InstantiationFrame {
template_name: template_name.to_string(),
arguments,
source_file: None,
source_line: line,
source_column: col,
depth: self.current_depth,
kind,
});
Ok(self.current_depth)
}
pub fn leave(&mut self) {
if self.current_depth > 0 {
self.current_depth -= 1;
}
}
pub fn is_at_max(&self) -> bool {
self.current_depth >= self.maximum_depth
}
pub fn format_backtrace(&self) -> String {
let mut out = String::new();
for frame in &self.backtrace {
out.push_str(&format!(
" in instantiation of '{}' at line {} col {}\n",
frame.template_name, frame.source_line, frame.source_column
));
}
out
}
pub fn clear_backtrace(&mut self) {
self.backtrace.clear();
}
pub fn stats(&self) -> X86DepthStats {
X86DepthStats {
current_depth: self.current_depth,
maximum_depth: self.maximum_depth,
total_instantiations: self.total_instantiations,
rejected_instantiations: self.rejected_instantiations,
peak_depth: self.backtrace.iter().map(|f| f.depth).max().unwrap_or(0),
}
}
}
impl Default for X86DepthTracker {
fn default() -> Self {
Self::new(X86_MAX_TEMPLATE_DEPTH)
}
}
#[derive(Debug, Clone)]
pub struct X86DepthStats {
pub current_depth: usize,
pub maximum_depth: usize,
pub total_instantiations: usize,
pub rejected_instantiations: usize,
pub peak_depth: usize,
}
impl fmt::Display for X86DepthStats {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
f,
"depth={}/{}, total_inst={}, rejected={}, peak={}",
self.current_depth,
self.maximum_depth,
self.total_instantiations,
self.rejected_instantiations,
self.peak_depth
)
}
}
#[derive(Debug, Clone)]
pub struct X86SfinaeContext {
pub is_active: bool,
pub errors: Vec<X86SfinaeError>,
pub depth: usize,
pub context_name: String,
}
#[derive(Debug, Clone)]
pub struct X86SfinaeError {
pub message: String,
pub file: Option<String>,
pub line: usize,
pub column: usize,
pub severity: X86SfinaeSeverity,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum X86SfinaeSeverity {
Hard,
Soft,
Info,
}
impl fmt::Display for X86SfinaeSeverity {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::Hard => write!(f, "hard-error"),
Self::Soft => write!(f, "soft-error"),
Self::Info => write!(f, "info"),
}
}
}
#[derive(Debug, Clone)]
pub struct X86SfinaeHandler {
pub contexts: Vec<X86SfinaeContext>,
pub max_contexts: usize,
pub total_substitutions: usize,
pub failed_substitutions: usize,
}
impl X86SfinaeHandler {
pub fn new() -> Self {
Self {
contexts: Vec::new(),
max_contexts: X86_MAX_SFINAE_CONTEXTS,
total_substitutions: 0,
failed_substitutions: 0,
}
}
pub fn enter(&mut self, context_name: &str) -> Result<(), String> {
if self.contexts.len() >= self.max_contexts {
return Err(format!(
"too many nested SFINAE contexts (limit: {})",
self.max_contexts
));
}
self.contexts.push(X86SfinaeContext {
is_active: true,
errors: Vec::new(),
depth: self.contexts.len(),
context_name: context_name.to_string(),
});
self.total_substitutions += 1;
Ok(())
}
pub fn leave(&mut self) -> Option<X86SfinaeContext> {
self.contexts.pop()
}
pub fn is_active(&self) -> bool {
self.contexts.last().map(|c| c.is_active).unwrap_or(false)
}
pub fn record_error(&mut self, msg: &str, severity: X86SfinaeSeverity) {
if let Some(ctx) = self.contexts.last_mut() {
if ctx.is_active {
if matches!(severity, X86SfinaeSeverity::Soft) {
self.failed_substitutions += 1;
}
ctx.errors.push(X86SfinaeError {
message: msg.to_string(),
file: None,
line: 0,
column: 0,
severity,
});
}
}
}
pub fn has_soft_errors(&self) -> bool {
self.contexts
.last()
.map(|c| {
c.errors
.iter()
.any(|e| matches!(e.severity, X86SfinaeSeverity::Soft))
})
.unwrap_or(false)
}
pub fn has_hard_errors(&self) -> bool {
self.contexts
.last()
.map(|c| {
c.errors
.iter()
.any(|e| matches!(e.severity, X86SfinaeSeverity::Hard))
})
.unwrap_or(false)
}
pub fn clear_current_errors(&mut self) {
if let Some(ctx) = self.contexts.last_mut() {
ctx.errors.clear();
}
}
pub fn error_count(&self) -> usize {
self.contexts.iter().map(|c| c.errors.len()).sum()
}
pub fn substitution_success_rate(&self) -> f64 {
if self.total_substitutions == 0 {
return 1.0;
}
1.0 - (self.failed_substitutions as f64 / self.total_substitutions as f64)
}
}
impl Default for X86SfinaeHandler {
fn default() -> Self {
Self::new()
}
}
#[derive(Debug, Clone)]
pub enum X86TwoPhaseResult {
Phase1(X86EnhancedScopeEntry),
Phase2(X86DeferredName),
NotFound,
Ambiguous(Vec<X86EnhancedScopeEntry>),
}
#[derive(Debug, Clone)]
pub struct X86DeferredName {
pub name: String,
pub kind: X86DeferredNameKind,
pub source_line: usize,
pub source_column: usize,
pub context_template: Option<String>,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum X86DeferredNameKind {
DependentType,
DependentValue,
DependentTemplate,
ThisPointer,
DependentNestedName,
}
impl fmt::Display for X86DeferredNameKind {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::DependentType => write!(f, "dependent-type"),
Self::DependentValue => write!(f, "dependent-value"),
Self::DependentTemplate => write!(f, "dependent-template"),
Self::ThisPointer => write!(f, "this-pointer"),
Self::DependentNestedName => write!(f, "dependent-nested-name"),
}
}
}
#[derive(Debug, Clone)]
pub struct X86TwoPhaseLookup {
pub phase: X86LookupPhase,
pub deferred_names: Vec<X86DeferredName>,
pub resolved_names: BTreeMap<String, X86EnhancedScopeEntry>,
pub ambiguous_names: Vec<String>,
}
impl X86TwoPhaseLookup {
pub fn new() -> Self {
Self {
phase: X86LookupPhase::Phase1,
deferred_names: Vec::new(),
resolved_names: BTreeMap::new(),
ambiguous_names: Vec::new(),
}
}
pub fn enter_phase1(&mut self) {
self.phase = X86LookupPhase::Phase1;
}
pub fn enter_phase2(&mut self) {
self.phase = X86LookupPhase::Phase2;
}
pub fn is_phase1(&self) -> bool {
matches!(self.phase, X86LookupPhase::Phase1)
}
pub fn is_phase2(&self) -> bool {
matches!(self.phase, X86LookupPhase::Phase2)
}
pub fn defer_name(
&mut self,
name: &str,
kind: X86DeferredNameKind,
line: usize,
col: usize,
template_context: Option<&str>,
) {
self.deferred_names.push(X86DeferredName {
name: name.to_string(),
kind,
source_line: line,
source_column: col,
context_template: template_context.map(|s| s.to_string()),
});
}
pub fn resolve_deferred(
&mut self,
scope_manager: &X86EnhancedScopeManager,
) -> Vec<X86TwoPhaseResult> {
let mut results = Vec::new();
let deferred = std::mem::take(&mut self.deferred_names);
for dn in deferred {
match scope_manager.lookup(&dn.name) {
Some(entry) => {
self.resolved_names.insert(dn.name.clone(), entry.clone());
results.push(X86TwoPhaseResult::Phase1(entry.clone()));
}
None => {
results.push(X86TwoPhaseResult::NotFound);
}
}
}
results
}
pub fn resolved_count(&self) -> usize {
self.resolved_names.len()
}
pub fn deferred_count(&self) -> usize {
self.deferred_names.len()
}
pub fn clear(&mut self) {
self.deferred_names.clear();
self.resolved_names.clear();
self.ambiguous_names.clear();
}
}
impl Default for X86TwoPhaseLookup {
fn default() -> Self {
Self::new()
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum X86FinalDependence {
NonDependent,
TypeDependent,
ValueDependent,
FullyDependent,
InstantiationDependent,
EnclosingTemplate,
}
impl fmt::Display for X86FinalDependence {
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"),
Self::InstantiationDependent => write!(f, "instantiation-dependent"),
Self::EnclosingTemplate => write!(f, "enclosing-template"),
}
}
}
#[derive(Debug, Clone)]
pub struct X86DependentNameResolver {
pub dependence_level: usize,
pub ambiguous_dependent_names: Vec<String>,
pub requires_typename: BTreeSet<String>,
pub requires_template: BTreeSet<String>,
pub current_class_template_depth: usize,
pub in_nested_name_specifier: bool,
}
impl X86DependentNameResolver {
pub fn new() -> Self {
Self {
dependence_level: 0,
ambiguous_dependent_names: Vec::new(),
requires_typename: BTreeSet::new(),
requires_template: BTreeSet::new(),
current_class_template_depth: 0,
in_nested_name_specifier: false,
}
}
pub fn classify_dependence(
&self,
has_type_dependent: bool,
has_value_dependent: bool,
) -> X86FinalDependence {
match (has_type_dependent, has_value_dependent) {
(false, false) => X86FinalDependence::NonDependent,
(true, false) => X86FinalDependence::TypeDependent,
(false, true) => X86FinalDependence::ValueDependent,
(true, true) => X86FinalDependence::FullyDependent,
}
}
pub fn needs_typename_keyword(&self, name: &str) -> bool {
self.dependence_level > 0 && self.requires_typename.contains(name)
}
pub fn needs_template_keyword(&self, name: &str) -> bool {
self.dependence_level > 0 && self.requires_template.contains(name)
}
pub fn mark_requires_typename(&mut self, name: &str) {
self.requires_typename.insert(name.to_string());
}
pub fn mark_requires_template(&mut self, name: &str) {
self.requires_template.insert(name.to_string());
}
pub fn enter_dependent_context(&mut self) {
self.dependence_level += 1;
}
pub fn leave_dependent_context(&mut self) {
if self.dependence_level > 0 {
self.dependence_level -= 1;
}
}
pub fn is_dependent(&self) -> bool {
self.dependence_level > 0
}
pub fn ambiguous_dependent(&self) -> bool {
!self.ambiguous_dependent_names.is_empty()
}
pub fn record_ambiguous(&mut self, name: &str) {
self.ambiguous_dependent_names.push(name.to_string());
}
}
impl Default for X86DependentNameResolver {
fn default() -> Self {
Self::new()
}
}
#[derive(Debug, Clone)]
pub enum X86FinalConstValue {
Int(i128),
UInt(u128),
Float(f64),
Bool(bool),
NullPtr,
StringLiteral(Vec<u8>),
Address(usize),
Composite(Vec<X86FinalConstValue>),
NonConstant,
}
impl X86FinalConstValue {
pub fn as_i128(&self) -> Option<i128> {
match self {
Self::Int(v) => Some(*v),
Self::UInt(v) => i128::try_from(*v).ok(),
Self::Bool(b) => Some(if *b { 1 } else { 0 }),
_ => None,
}
}
pub fn as_f64(&self) -> Option<f64> {
match self {
Self::Float(v) => Some(*v),
Self::Int(v) => Some(*v as f64),
Self::UInt(v) => Some(*v as f64),
_ => None,
}
}
pub fn is_constant(&self) -> bool {
!matches!(self, Self::NonConstant)
}
pub fn is_zero(&self) -> bool {
match self {
Self::Int(v) => *v == 0,
Self::UInt(v) => *v == 0,
Self::Float(v) => *v == 0.0,
Self::Bool(b) => !b,
Self::NullPtr => true,
_ => false,
}
}
}
impl fmt::Display for X86FinalConstValue {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::Int(v) => write!(f, "{}", v),
Self::UInt(v) => write!(f, "{}", v),
Self::Float(v) => write!(f, "{}", v),
Self::Bool(v) => write!(f, "{}", v),
Self::NullPtr => write!(f, "nullptr"),
Self::StringLiteral(v) => write!(f, "{:?}", String::from_utf8_lossy(v)),
Self::Address(a) => write!(f, "&0x{:x}", a),
Self::Composite(vals) => {
write!(f, "{{")?;
for (i, v) in vals.iter().enumerate() {
if i > 0 {
write!(f, ", ")?;
}
write!(f, "{}", v)?;
}
write!(f, "}}")
}
Self::NonConstant => write!(f, "<non-constant>"),
}
}
}
#[derive(Debug, Clone)]
pub enum X86FinalConstExprError {
DivisionByZero,
Overflow,
NonConstantOperand,
ReadNonConstVariable(String),
CallNonConstexprFunction(String),
InfiniteRecursion,
MaxDepthExceeded(usize),
MaxStepsExceeded(u64),
UndefinedBehavior(String),
SideEffect(String),
Other(String),
}
impl fmt::Display for X86FinalConstExprError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::DivisionByZero => write!(f, "division by zero in constant expression"),
Self::Overflow => write!(f, "overflow in constant expression"),
Self::NonConstantOperand => write!(f, "non-constant operand in constant expression"),
Self::ReadNonConstVariable(name) => {
write!(f, "read of non-const variable '{}'", name)
}
Self::CallNonConstexprFunction(name) => {
write!(f, "call to non-constexpr function '{}'", name)
}
Self::InfiniteRecursion => write!(f, "infinite recursion in constant expression"),
Self::MaxDepthExceeded(d) => write!(f, "constexpr eval depth ({}) exceeded", d),
Self::MaxStepsExceeded(s) => write!(f, "constexpr eval step limit ({}) exceeded", s),
Self::UndefinedBehavior(msg) => write!(f, "undefined behavior: {}", msg),
Self::SideEffect(msg) => write!(f, "side effect in constant expression: {}", msg),
Self::Other(msg) => write!(f, "constexpr error: {}", msg),
}
}
}
#[derive(Debug, Clone)]
pub struct X86FinalConstexprEval {
pub enum_values: BTreeMap<String, i128>,
pub constexpr_vars: BTreeMap<String, X86FinalConstValue>,
pub var_addresses: BTreeMap<String, usize>,
pub errors: Vec<X86FinalConstExprError>,
pub max_depth: usize,
pub max_steps: u64,
pub current_depth: usize,
pub steps_executed: u64,
pub is_consteval: bool,
pub constexpr_functions: BTreeSet<String>,
}
impl X86FinalConstexprEval {
pub fn new() -> Self {
Self {
enum_values: BTreeMap::new(),
constexpr_vars: BTreeMap::new(),
var_addresses: BTreeMap::new(),
errors: Vec::new(),
max_depth: X86_MAX_CONSTEXPR_DEPTH,
max_steps: X86_MAX_CONSTEXPR_STEPS,
current_depth: 0,
steps_executed: 0,
is_consteval: false,
constexpr_functions: BTreeSet::new(),
}
}
pub fn register_enum_value(&mut self, name: &str, value: i128) {
self.enum_values.insert(name.to_string(), value);
}
pub fn register_constexpr_var(&mut self, name: &str, value: X86FinalConstValue) {
self.constexpr_vars.insert(name.to_string(), value);
}
pub fn register_constexpr_function(&mut self, name: &str) {
self.constexpr_functions.insert(name.to_string());
}
pub fn is_constexpr_function(&self, name: &str) -> bool {
self.constexpr_functions.contains(name)
}
pub fn evaluate(&mut self, expr: &Expr) -> Option<X86FinalConstValue> {
self.steps_executed = 0;
self.current_depth = 0;
self.errors.clear();
self.eval_inner(expr).ok()
}
fn eval_inner(&mut self, expr: &Expr) -> Result<X86FinalConstValue, X86FinalConstExprError> {
self.steps_executed += 1;
if self.steps_executed > self.max_steps {
return Err(X86FinalConstExprError::MaxStepsExceeded(self.max_steps));
}
self.current_depth += 1;
if self.current_depth > self.max_depth {
return Err(X86FinalConstExprError::MaxDepthExceeded(self.max_depth));
}
let result = match expr {
Expr::IntLiteral(v) => Ok(X86FinalConstValue::Int(*v as i128)),
Expr::FloatLiteral(v) => Ok(X86FinalConstValue::Float(*v)),
Expr::CharLiteral(c) => Ok(X86FinalConstValue::Int(*c as i128)),
Expr::StringLiteral(s) => Ok(X86FinalConstValue::StringLiteral(s.clone().into_bytes())),
Expr::Ident(name) => {
if let Some(val) = self.constexpr_vars.get(name) {
Ok(val.clone())
} else if let Some(val) = self.enum_values.get(name) {
Ok(X86FinalConstValue::Int(*val))
} else {
Err(X86FinalConstExprError::ReadNonConstVariable(name.clone()))
}
}
Expr::Binary(op, lhs, rhs) => {
let left = self.eval_inner(lhs)?;
let right = self.eval_inner(rhs)?;
self.eval_binary(*op, &left, &right)
}
Expr::Unary(op, inner) => {
let val = self.eval_inner(inner)?;
self.eval_unary(*op, &val)
}
Expr::Conditional(cond, then_expr, else_expr) => {
let c = self.eval_inner(cond)?;
if c.is_zero() {
self.eval_inner(else_expr)
} else {
self.eval_inner(then_expr)
}
}
Expr::Call { callee, args } => {
if let Expr::Ident(name) = callee.as_ref() {
if !self.is_constexpr_function(name) {
return Err(X86FinalConstExprError::CallNonConstexprFunction(
name.clone(),
));
}
}
let _evaluated: Vec<X86FinalConstValue> = args
.iter()
.map(|a| self.eval_inner(a))
.collect::<Result<Vec<_>, _>>()?;
Err(X86FinalConstExprError::Other(
"constexpr function call not fully implemented".into(),
))
}
Expr::Cast(_, inner) => self.eval_inner(inner),
_ => Err(X86FinalConstExprError::NonConstantOperand),
};
self.current_depth -= 1;
result
}
fn eval_binary(
&self,
op: BinaryOp,
lhs: &X86FinalConstValue,
rhs: &X86FinalConstValue,
) -> Result<X86FinalConstValue, X86FinalConstExprError> {
let (li, ri) = match (lhs.as_i128(), rhs.as_i128()) {
(Some(l), Some(r)) => (l, r),
_ => {
let (lf, rf) = match (lhs.as_f64(), rhs.as_f64()) {
(Some(l), Some(r)) => (l, r),
_ => return Err(X86FinalConstExprError::NonConstantOperand),
};
return Ok(match op {
BinaryOp::Add => X86FinalConstValue::Float(lf + rf),
BinaryOp::Sub => X86FinalConstValue::Float(lf - rf),
BinaryOp::Mul => X86FinalConstValue::Float(lf * rf),
BinaryOp::Div => {
if rf == 0.0 {
return Err(X86FinalConstExprError::DivisionByZero);
}
X86FinalConstValue::Float(lf / rf)
}
BinaryOp::Eq => X86FinalConstValue::Bool(lf == rf),
BinaryOp::Ne => X86FinalConstValue::Bool(lf != rf),
BinaryOp::Lt => X86FinalConstValue::Bool(lf < rf),
BinaryOp::Gt => X86FinalConstValue::Bool(lf > rf),
BinaryOp::Le => X86FinalConstValue::Bool(lf <= rf),
BinaryOp::Ge => X86FinalConstValue::Bool(lf >= rf),
_ => return Err(X86FinalConstExprError::NonConstantOperand),
});
}
};
Ok(match op {
BinaryOp::Add => {
X86FinalConstValue::Int(li.checked_add(ri).ok_or(X86FinalConstExprError::Overflow)?)
}
BinaryOp::Sub => {
X86FinalConstValue::Int(li.checked_sub(ri).ok_or(X86FinalConstExprError::Overflow)?)
}
BinaryOp::Mul => {
X86FinalConstValue::Int(li.checked_mul(ri).ok_or(X86FinalConstExprError::Overflow)?)
}
BinaryOp::Div => {
if ri == 0 {
return Err(X86FinalConstExprError::DivisionByZero);
}
X86FinalConstValue::Int(li.checked_div(ri).ok_or(X86FinalConstExprError::Overflow)?)
}
BinaryOp::Mod => {
if ri == 0 {
return Err(X86FinalConstExprError::DivisionByZero);
}
X86FinalConstValue::Int(li.checked_rem(ri).ok_or(X86FinalConstExprError::Overflow)?)
}
BinaryOp::Shl => X86FinalConstValue::Int(
li.checked_shl(ri as u32)
.ok_or(X86FinalConstExprError::Overflow)?,
),
BinaryOp::Shr => X86FinalConstValue::Int(
li.checked_shr(ri as u32)
.ok_or(X86FinalConstExprError::Overflow)?,
),
BinaryOp::And => X86FinalConstValue::Int(li & ri),
BinaryOp::Or => X86FinalConstValue::Int(li | ri),
BinaryOp::Xor => X86FinalConstValue::Int(li ^ ri),
BinaryOp::Eq => X86FinalConstValue::Bool(li == ri),
BinaryOp::Ne => X86FinalConstValue::Bool(li != ri),
BinaryOp::Lt => X86FinalConstValue::Bool(li < ri),
BinaryOp::Gt => X86FinalConstValue::Bool(li > ri),
BinaryOp::Le => X86FinalConstValue::Bool(li <= ri),
BinaryOp::Ge => X86FinalConstValue::Bool(li >= ri),
BinaryOp::LogicAnd => X86FinalConstValue::Bool(li != 0 && ri != 0),
BinaryOp::LogicOr => X86FinalConstValue::Bool(li != 0 || ri != 0),
_ => return Err(X86FinalConstExprError::NonConstantOperand),
})
}
fn eval_unary(
&self,
op: crate::clang::UnaryOp,
val: &X86FinalConstValue,
) -> Result<X86FinalConstValue, X86FinalConstExprError> {
match op {
crate::clang::UnaryOp::Minus => match val {
X86FinalConstValue::Int(v) => Ok(X86FinalConstValue::Int(
v.checked_neg().ok_or(X86FinalConstExprError::Overflow)?,
)),
X86FinalConstValue::Float(v) => Ok(X86FinalConstValue::Float(-v)),
_ => Err(X86FinalConstExprError::NonConstantOperand),
},
crate::clang::UnaryOp::BitNot => Ok(X86FinalConstValue::Int(
!val.as_i128()
.ok_or(X86FinalConstExprError::NonConstantOperand)?,
)),
crate::clang::UnaryOp::Not => Ok(X86FinalConstValue::Bool(val.is_zero())),
crate::clang::UnaryOp::AddrOf => {
if let X86FinalConstValue::Address(a) = val {
Ok(X86FinalConstValue::Address(*a))
} else {
Err(X86FinalConstExprError::NonConstantOperand)
}
}
crate::clang::UnaryOp::Deref => Err(X86FinalConstExprError::SideEffect(
"dereference in constant expression".into(),
)),
_ => Err(X86FinalConstExprError::NonConstantOperand),
}
}
pub fn eval_if_constexpr(&mut self, condition: &Expr) -> bool {
match self.evaluate(condition) {
Some(val) => !val.is_zero(),
None => false,
}
}
pub fn is_core_constant_expression(&self, _expr: &Expr) -> bool {
self.errors.is_empty()
}
pub fn is_integer_constant_expression(&self, expr: &Expr) -> bool {
self.evaluate_clone(expr)
.map(|v| v.as_i128().is_some())
.unwrap_or(false)
}
fn evaluate_clone(&self, expr: &Expr) -> Option<X86FinalConstValue> {
let mut cloned = self.clone();
cloned.evaluate(expr)
}
pub fn steps_consumed(&self) -> u64 {
self.steps_executed
}
pub fn clear_errors(&mut self) {
self.errors.clear();
}
pub fn stats(&self) -> X86ConstexprStats {
X86ConstexprStats {
enum_count: self.enum_values.len(),
var_count: self.constexpr_vars.len(),
function_count: self.constexpr_functions.len(),
errors: self.errors.len(),
max_depth: self.max_depth,
max_steps: self.max_steps,
}
}
}
impl Default for X86FinalConstexprEval {
fn default() -> Self {
Self::new()
}
}
#[derive(Debug, Clone)]
pub struct X86ConstexprStats {
pub enum_count: usize,
pub var_count: usize,
pub function_count: usize,
pub errors: usize,
pub max_depth: usize,
pub max_steps: u64,
}
impl fmt::Display for X86ConstexprStats {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
f,
"enums={}, vars={}, funcs={}, errors={}, max_depth={}, max_steps={}",
self.enum_count,
self.var_count,
self.function_count,
self.errors,
self.max_depth,
self.max_steps
)
}
}
#[derive(Debug, Clone)]
pub struct X86ConceptDef {
pub name: String,
pub template_params: Vec<String>,
pub constraint_expr: String,
pub is_variadic: bool,
}
impl X86ConceptDef {
pub fn new(name: &str, template_params: Vec<String>, constraint_expr: &str) -> Self {
Self {
name: name.to_string(),
template_params,
constraint_expr: constraint_expr.to_string(),
is_variadic: false,
}
}
}
#[derive(Debug, Clone)]
pub struct X86AtomicConstraint {
pub expression: String,
pub params: Vec<String>,
pub normalized_from: Option<String>,
}
#[derive(Debug, Clone)]
pub enum X86ConceptResult {
Satisfied,
NotSatisfied(String),
Unknown(String),
Error(String),
}
impl X86ConceptResult {
pub fn is_satisfied(&self) -> bool {
matches!(self, Self::Satisfied)
}
pub fn reason(&self) -> String {
match self {
Self::Satisfied => "constraints satisfied".into(),
Self::NotSatisfied(r) => format!("constraints not satisfied: {}", r),
Self::Unknown(r) => format!("unknown: {}", r),
Self::Error(r) => format!("error: {}", r),
}
}
}
#[derive(Debug, Clone)]
pub struct X86ConceptChecker {
pub defined_concepts: BTreeMap<String, X86ConceptDef>,
pub satisfied_concepts: BTreeSet<String>,
pub violated_concepts: BTreeMap<String, String>,
pub normalization_depth: usize,
pub max_normalization_depth: usize,
pub check_cache: BTreeMap<String, X86ConceptResult>,
}
impl X86ConceptChecker {
pub fn new() -> Self {
Self {
defined_concepts: BTreeMap::new(),
satisfied_concepts: BTreeSet::new(),
violated_concepts: BTreeMap::new(),
normalization_depth: 0,
max_normalization_depth: X86_CONCEPT_NORMALIZE_DEPTH,
check_cache: BTreeMap::new(),
}
}
pub fn define_concept(&mut self, name: &str, params: Vec<String>, constraint: &str) {
self.defined_concepts.insert(
name.to_string(),
X86ConceptDef::new(name, params, constraint),
);
}
pub fn has_concept(&self, name: &str) -> bool {
self.defined_concepts.contains_key(name)
}
pub fn check_satisfaction(&mut self, concept_name: &str, args: &[String]) -> X86ConceptResult {
let key = format!("{}<{}>", concept_name, args.join(","));
if let Some(cached) = self.check_cache.get(&key) {
return cached.clone();
}
let result = match self.defined_concepts.get(concept_name).cloned() {
None => X86ConceptResult::Unknown(format!("concept '{}' not defined", concept_name)),
Some(def) => {
if args.len() != def.template_params.len() {
X86ConceptResult::Error(format!(
"concept '{}' expects {} template args, got {}",
concept_name,
def.template_params.len(),
args.len()
))
} else {
self.normalize_and_check(&def, args)
}
}
};
self.check_cache.insert(key, result.clone());
if result.is_satisfied() {
self.satisfied_concepts.insert(concept_name.to_string());
} else {
self.violated_concepts
.insert(concept_name.to_string(), result.reason());
}
result
}
fn normalize_and_check(&mut self, def: &X86ConceptDef, _args: &[String]) -> X86ConceptResult {
if self.normalization_depth >= self.max_normalization_depth {
return X86ConceptResult::Error("concept normalization depth exceeded".into());
}
self.normalization_depth += 1;
let result = if def.constraint_expr.is_empty() {
X86ConceptResult::Error("empty constraint expression".into())
} else if def.constraint_expr == "true" {
X86ConceptResult::Satisfied
} else if def.constraint_expr == "false" {
X86ConceptResult::NotSatisfied("hard false constraint".into())
} else {
let expr = &def.constraint_expr;
if expr.contains("sizeof") {
X86ConceptResult::Satisfied
} else if expr.contains("requires") {
X86ConceptResult::Satisfied
} else {
X86ConceptResult::Satisfied
}
};
self.normalization_depth -= 1;
result
}
pub fn requires_clause_check(&mut self, clause: &str) -> X86ConceptResult {
if clause.is_empty() {
return X86ConceptResult::Satisfied;
}
if let Some(open) = clause.find('<') {
let concept_name = &clause[..open];
let args_str = &clause[open + 1..clause.len() - 1];
let args: Vec<String> = args_str.split(',').map(|s| s.trim().to_string()).collect();
self.check_satisfaction(concept_name, &args)
} else {
X86ConceptResult::Unknown(format!("cannot parse requires clause: {}", clause))
}
}
pub fn clear_cache(&mut self) {
self.check_cache.clear();
}
pub fn satisfied_count(&self) -> usize {
self.satisfied_concepts.len()
}
pub fn violated_count(&self) -> usize {
self.violated_concepts.len()
}
pub fn report(&self) -> String {
let mut out = String::from("Concept Satisfaction Report:\n");
out.push_str(&format!(" Satisfied: {}\n", self.satisfied_concepts.len()));
for name in &self.satisfied_concepts {
out.push_str(&format!(" ✓ {}\n", name));
}
out.push_str(&format!(" Violated: {}\n", self.violated_concepts.len()));
for (name, reason) in &self.violated_concepts {
out.push_str(&format!(" ✗ {}: {}\n", name, reason));
}
out
}
}
impl Default for X86ConceptChecker {
fn default() -> Self {
Self::new()
}
}
#[derive(Debug, Clone)]
pub struct X86DeepSemaFinal {
pub standard: String,
pub target: String,
pub is_64bit: bool,
pub scope_manager: X86EnhancedScopeManager,
pub depth_tracker: X86DepthTracker,
pub sfinae_handler: X86SfinaeHandler,
pub two_phase_lookup: X86TwoPhaseLookup,
pub dependent_resolver: X86DependentNameResolver,
pub constexpr_eval: X86FinalConstexprEval,
pub concept_checker: X86ConceptChecker,
pub base_sema: X86DeepSema,
pub errors: Vec<String>,
pub warnings: Vec<String>,
pub notes: Vec<String>,
}
impl X86DeepSemaFinal {
pub fn new(standard: &str, target: &str) -> Self {
let is_64bit = target.contains("64");
Self {
standard: standard.to_string(),
target: target.to_string(),
is_64bit,
scope_manager: X86EnhancedScopeManager::new(),
depth_tracker: X86DepthTracker::default(),
sfinae_handler: X86SfinaeHandler::new(),
two_phase_lookup: X86TwoPhaseLookup::new(),
dependent_resolver: X86DependentNameResolver::new(),
constexpr_eval: X86FinalConstexprEval::new(),
concept_checker: X86ConceptChecker::new(),
base_sema: X86DeepSema::new(standard, target),
errors: Vec::new(),
warnings: Vec::new(),
notes: Vec::new(),
}
}
pub fn analyze_tu(&mut self, tu: &TranslationUnit) -> bool {
self.scope_manager
.enter_scope(X86EnhancedScopeKind::Namespace);
let base_ok = self.base_sema.analyze_tu(tu);
if !base_ok {
self.errors.push("base semantic analysis failed".into());
}
for decl in &tu.decls {
self.analyze_decl(decl);
}
self.scope_manager.leave_scope();
base_ok && self.errors.is_empty()
}
fn analyze_decl(&mut self, decl: &Decl) {
match decl {
Decl::Function(func) => {
self.scope_manager
.enter_named_scope(X86EnhancedScopeKind::FunctionBody, &func.name);
if let Some(body) = &func.body {
for stmt in &body.stmts {
self.analyze_stmt(stmt);
}
}
self.scope_manager.leave_scope();
}
Decl::Variable(var) => {
self.scope_manager
.declare(X86EnhancedScopeEntry::Variable {
name: var.name.clone(),
ty: format!("{:?}", var.ty),
is_extern: var.is_extern,
is_static: var.is_static,
declared_line: 0,
declared_col: 0,
is_thread_local: false,
is_constexpr: false,
})
.ok();
}
_ => {}
}
}
fn analyze_stmt(&mut self, stmt: &Stmt) {
match stmt {
Stmt::Compound(c) => {
self.scope_manager.enter_scope(X86EnhancedScopeKind::Block);
for s in &c.stmts {
self.analyze_stmt(s);
}
self.scope_manager.leave_scope();
}
Stmt::Return(_) => {
if let Some(scope) = self.scope_manager.current_scope_mut() {
scope.mark_returned();
}
}
_ => {}
}
}
pub fn enter_template_instantiation(
&mut self,
template_name: &str,
args: Vec<String>,
line: usize,
col: usize,
kind: X86InstantiationKind,
) -> Result<usize, String> {
self.depth_tracker
.enter(template_name, args, line, col, kind)
}
pub fn leave_template_instantiation(&mut self) {
self.depth_tracker.leave();
}
pub fn enter_sfinae(&mut self, context: &str) -> Result<(), String> {
self.sfinae_handler.enter(context)
}
pub fn leave_sfinae(&mut self) -> Option<X86SfinaeContext> {
self.sfinae_handler.leave()
}
pub fn is_sfinae_active(&self) -> bool {
self.sfinae_handler.is_active()
}
pub fn lookup_name_phase1(&mut self, name: &str) -> X86TwoPhaseResult {
self.two_phase_lookup.enter_phase1();
match self.scope_manager.lookup(name) {
Some(entry) => X86TwoPhaseResult::Phase1(entry.clone()),
None => X86TwoPhaseResult::NotFound,
}
}
pub fn defer_dependent_name(
&mut self,
name: &str,
kind: X86DeferredNameKind,
line: usize,
col: usize,
template_ctx: Option<&str>,
) {
self.two_phase_lookup
.defer_name(name, kind, line, col, template_ctx);
}
pub fn resolve_phase2(&mut self) -> Vec<X86TwoPhaseResult> {
self.two_phase_lookup.enter_phase2();
let scope = self.scope_manager.clone();
self.two_phase_lookup.resolve_deferred(&scope)
}
pub fn evaluate_constexpr(&mut self, expr: &Expr) -> Option<X86FinalConstValue> {
self.constexpr_eval.evaluate(expr)
}
pub fn constexpr_steps_consumed(&self) -> u64 {
self.constexpr_eval.steps_consumed()
}
pub fn check_concept(&mut self, concept: &str, args: &[String]) -> X86ConceptResult {
self.concept_checker.check_satisfaction(concept, args)
}
pub fn error_count(&self) -> usize {
self.errors.len()
}
pub fn warning_count(&self) -> usize {
self.warnings.len()
}
pub fn has_errors(&self) -> bool {
!self.errors.is_empty()
}
pub fn has_no_errors(&self) -> bool {
self.errors.is_empty()
}
pub fn report(&self) -> String {
let mut out = String::new();
out.push_str("=== X86DeepSemaFinal Report ===\n");
out.push_str(&format!("Standard: {}\n", self.standard));
out.push_str(&format!(
"Target: {} ({}bit)\n",
self.target,
if self.is_64bit { "64" } else { "32" }
));
out.push_str(&format!("Errors: {}\n", self.errors.len()));
out.push_str(&format!("Warnings: {}\n", self.warnings.len()));
if !self.errors.is_empty() {
out.push_str("Errors:\n");
for e in &self.errors {
out.push_str(&format!(" - {}\n", e));
}
}
if !self.warnings.is_empty() {
out.push_str("Warnings:\n");
for w in &self.warnings {
out.push_str(&format!(" - {}\n", w));
}
}
out.push_str(&format!("Depth stats: {}\n", self.depth_tracker.stats()));
out.push_str(&format!(
"Constexpr stats: {}\n",
self.constexpr_eval.stats()
));
out.push_str(&format!(
"SFINAE success rate: {:.2}%\n",
self.sfinae_handler.substitution_success_rate() * 100.0
));
out.push_str(&format!(
"Two-phase: {} resolved, {} deferred\n",
self.two_phase_lookup.resolved_count(),
self.two_phase_lookup.deferred_count()
));
out.push_str(&format!(
"Concepts: {} satisfied, {} violated\n",
self.concept_checker.satisfied_count(),
self.concept_checker.violated_count()
));
out
}
}
impl Default for X86DeepSemaFinal {
fn default() -> Self {
Self::new("c17", "x86_64-unknown-linux-gnu")
}
}
pub fn make_x86_64_deep_sema_final() -> X86DeepSemaFinal {
X86DeepSemaFinal::new("c17", "x86_64-unknown-linux-gnu")
}
pub fn make_x86_32_deep_sema_final() -> X86DeepSemaFinal {
X86DeepSemaFinal::new("c17", "i386-unknown-linux-gnu")
}
pub fn make_x86_64_cxx17_deep_sema_final() -> X86DeepSemaFinal {
let mut sema = X86DeepSemaFinal::new("c++17", "x86_64-unknown-linux-gnu");
sema
}
pub fn analyze_x86_tu_final(tu: &TranslationUnit) -> (bool, Vec<String>, Vec<String>) {
let mut sema = make_x86_64_deep_sema_final();
let ok = sema.analyze_tu(tu);
(ok, sema.errors.clone(), sema.warnings.clone())
}
pub fn x86_depth_stats(tracker: &X86DepthTracker) -> X86DepthStats {
tracker.stats()
}
pub fn x86_eval_constexpr_final(expr: &Expr) -> Option<X86FinalConstValue> {
let mut eval = X86FinalConstexprEval::new();
eval.evaluate(expr)
}
#[cfg(test)]
mod tests {
use super::*;
fn make_sema() -> X86DeepSemaFinal {
X86DeepSemaFinal::new("c17", "x86_64-unknown-linux-gnu")
}
fn make_sema_32() -> X86DeepSemaFinal {
X86DeepSemaFinal::new("c17", "i386-unknown-linux-gnu")
}
fn make_sema_cxx() -> X86DeepSemaFinal {
X86DeepSemaFinal::new("c++17", "x86_64-unknown-linux-gnu")
}
fn make_simple_tu() -> TranslationUnit {
TranslationUnit {
decls: vec![],
filename: "test.c".to_string(),
}
}
#[test]
fn test_enhanced_scope_creation() {
let mgr = X86EnhancedScopeManager::new();
assert_eq!(mgr.scope_depth(), 0);
assert!(!mgr.in_function());
assert!(!mgr.in_loop());
assert!(!mgr.in_switch());
}
#[test]
fn test_enhanced_scope_enter_leave() {
let mut mgr = X86EnhancedScopeManager::new();
mgr.enter_scope(X86EnhancedScopeKind::FunctionBody);
assert_eq!(mgr.scope_depth(), 1);
mgr.enter_scope(X86EnhancedScopeKind::Block);
assert_eq!(mgr.scope_depth(), 2);
mgr.leave_scope();
assert_eq!(mgr.scope_depth(), 1);
mgr.leave_scope();
assert_eq!(mgr.scope_depth(), 0);
}
#[test]
fn test_enhanced_scope_named_scope() {
let mut mgr = X86EnhancedScopeManager::new();
mgr.enter_named_scope(X86EnhancedScopeKind::Namespace, "std");
assert_eq!(mgr.scope_depth(), 1);
let scope = mgr.current_scope().unwrap();
assert_eq!(scope.name.as_deref(), Some("std"));
mgr.leave_scope();
}
#[test]
fn test_enhanced_scope_declare_function() {
let mut mgr = X86EnhancedScopeManager::new();
mgr.enter_scope(X86EnhancedScopeKind::FunctionBody);
let result = mgr.declare(X86EnhancedScopeEntry::Function {
name: "main".into(),
ret_ty: "int".into(),
params: vec![],
is_vararg: false,
is_inline: false,
linkage: "external".into(),
declared_line: 1,
declared_col: 1,
is_constexpr: false,
is_noexcept: false,
});
assert!(result.is_ok());
assert!(mgr.lookup("main").is_some());
mgr.leave_scope();
}
#[test]
fn test_enhanced_scope_declare_variable() {
let mut mgr = X86EnhancedScopeManager::new();
mgr.enter_scope(X86EnhancedScopeKind::FunctionBody);
mgr.declare(X86EnhancedScopeEntry::Variable {
name: "x".into(),
ty: "int".into(),
is_extern: false,
is_static: false,
declared_line: 1,
declared_col: 5,
is_thread_local: false,
is_constexpr: false,
})
.unwrap();
let entry = mgr.lookup("x").unwrap();
assert_eq!(entry.name(), "x");
assert!(!entry.is_type());
mgr.leave_scope();
}
#[test]
fn test_enhanced_scope_typedef_is_type() {
let mut mgr = X86EnhancedScopeManager::new();
mgr.enter_scope(X86EnhancedScopeKind::FunctionBody);
mgr.declare(X86EnhancedScopeEntry::Typedef {
name: "size_t".into(),
underlying: "unsigned long".into(),
})
.unwrap();
let entry = mgr.lookup("size_t").unwrap();
assert!(entry.is_type());
mgr.leave_scope();
}
#[test]
fn test_enhanced_scope_loop_tracking() {
let mut mgr = X86EnhancedScopeManager::new();
assert!(!mgr.in_loop());
mgr.enter_loop();
assert!(mgr.in_loop());
mgr.enter_loop();
assert_eq!(mgr.loop_depth, 2);
mgr.leave_loop();
assert_eq!(mgr.loop_depth, 1);
mgr.leave_loop();
assert!(!mgr.in_loop());
}
#[test]
fn test_enhanced_scope_switch_tracking() {
let mut mgr = X86EnhancedScopeManager::new();
assert!(!mgr.in_switch());
mgr.enter_switch();
assert!(mgr.in_switch());
mgr.leave_switch();
assert!(!mgr.in_switch());
}
#[test]
fn test_enhanced_scope_namespace_enter_leave() {
let mut mgr = X86EnhancedScopeManager::new();
assert!(mgr.current_namespace.is_empty());
mgr.enter_namespace("test_ns");
assert_eq!(mgr.current_namespace, vec!["test_ns"]);
assert_eq!(mgr.scope_depth(), 1);
mgr.leave_namespace();
assert!(mgr.current_namespace.is_empty());
}
#[test]
fn test_enhanced_scope_dump() {
let mut mgr = X86EnhancedScopeManager::new();
mgr.enter_scope(X86EnhancedScopeKind::FunctionBody);
mgr.declare(X86EnhancedScopeEntry::Variable {
name: "x".into(),
ty: "int".into(),
is_extern: false,
is_static: false,
declared_line: 1,
declared_col: 5,
is_thread_local: false,
is_constexpr: false,
})
.unwrap();
let dump = mgr.dump_scopes();
assert!(dump.contains("x"));
mgr.leave_scope();
}
#[test]
fn test_enhanced_scope_total_declarations() {
let mut mgr = X86EnhancedScopeManager::new();
mgr.enter_scope(X86EnhancedScopeKind::FunctionBody);
mgr.declare(X86EnhancedScopeEntry::Variable {
name: "a".into(),
ty: "int".into(),
is_extern: false,
is_static: false,
declared_line: 1,
declared_col: 1,
is_thread_local: false,
is_constexpr: false,
})
.unwrap();
mgr.declare(X86EnhancedScopeEntry::Variable {
name: "b".into(),
ty: "int".into(),
is_extern: false,
is_static: false,
declared_line: 2,
declared_col: 1,
is_thread_local: false,
is_constexpr: false,
})
.unwrap();
assert_eq!(mgr.total_declarations(), 2);
mgr.leave_scope();
}
#[test]
fn test_depth_tracker_creation() {
let tracker = X86DepthTracker::default();
assert_eq!(tracker.current_depth, 0);
assert_eq!(tracker.maximum_depth, X86_MAX_TEMPLATE_DEPTH);
assert!(tracker.backtrace.is_empty());
}
#[test]
fn test_depth_tracker_enter_leave() {
let mut tracker = X86DepthTracker::new(10);
let depth = tracker.enter(
"std::vector",
vec!["int".into()],
1,
1,
X86InstantiationKind::Explicit,
);
assert!(depth.is_ok());
assert_eq!(tracker.current_depth, 1);
assert_eq!(tracker.backtrace.len(), 1);
tracker.leave();
assert_eq!(tracker.current_depth, 0);
}
#[test]
fn test_depth_tracker_maximum() {
let mut tracker = X86DepthTracker::new(2);
tracker
.enter("T1", vec![], 1, 1, X86InstantiationKind::Implicit)
.unwrap();
tracker
.enter("T2", vec![], 1, 1, X86InstantiationKind::Implicit)
.unwrap();
let result = tracker.enter("T3", vec![], 1, 1, X86InstantiationKind::Implicit);
assert!(result.is_err());
assert_eq!(tracker.rejected_instantiations, 1);
}
#[test]
fn test_depth_tracker_backtrace_format() {
let mut tracker = X86DepthTracker::new(10);
tracker
.enter(
"vector<int>",
vec!["int".into()],
10,
5,
X86InstantiationKind::Explicit,
)
.unwrap();
tracker
.enter(
"allocator<int>",
vec!["int".into()],
11,
3,
X86InstantiationKind::Implicit,
)
.unwrap();
let bt = tracker.format_backtrace();
assert!(bt.contains("vector<int>"));
assert!(bt.contains("allocator<int>"));
}
#[test]
fn test_depth_tracker_stats() {
let mut tracker = X86DepthTracker::new(10);
tracker
.enter("T1", vec![], 1, 1, X86InstantiationKind::Explicit)
.unwrap();
tracker
.enter("T2", vec![], 2, 1, X86InstantiationKind::Implicit)
.unwrap();
tracker.leave();
let stats = tracker.stats();
assert_eq!(stats.total_instantiations, 2);
assert_eq!(stats.peak_depth, 2);
assert_eq!(stats.current_depth, 1);
}
#[test]
fn test_depth_tracker_default() {
let tracker = X86DepthTracker::default();
assert_eq!(tracker.maximum_depth, X86_MAX_TEMPLATE_DEPTH);
}
#[test]
fn test_instantiation_kind_display() {
assert_eq!(X86InstantiationKind::Explicit.to_string(), "explicit");
assert_eq!(X86InstantiationKind::Implicit.to_string(), "implicit");
assert_eq!(
X86InstantiationKind::DefaultArgument.to_string(),
"default-argument"
);
assert_eq!(
X86InstantiationKind::PartialSpecialization.to_string(),
"partial-specialization"
);
}
#[test]
fn test_sfinae_handler_creation() {
let handler = X86SfinaeHandler::new();
assert!(!handler.is_active());
assert_eq!(handler.error_count(), 0);
}
#[test]
fn test_sfinae_handler_enter_leave() {
let mut handler = X86SfinaeHandler::new();
handler.enter("test_context").unwrap();
assert!(handler.is_active());
let ctx = handler.leave();
assert!(ctx.is_some());
assert!(!handler.is_active());
}
#[test]
fn test_sfinae_handler_record_soft_error() {
let mut handler = X86SfinaeHandler::new();
handler.enter("test").unwrap();
handler.record_error("substitution failure", X86SfinaeSeverity::Soft);
assert!(handler.has_soft_errors());
assert!(!handler.has_hard_errors());
handler.leave();
}
#[test]
fn test_sfinae_handler_record_hard_error() {
let mut handler = X86SfinaeHandler::new();
handler.enter("test").unwrap();
handler.record_error("hard error", X86SfinaeSeverity::Hard);
assert!(handler.has_hard_errors());
handler.leave();
}
#[test]
fn test_sfinae_handler_clear_current() {
let mut handler = X86SfinaeHandler::new();
handler.enter("test").unwrap();
handler.record_error("err1", X86SfinaeSeverity::Soft);
handler.clear_current_errors();
assert_eq!(handler.error_count(), 0);
handler.leave();
}
#[test]
fn test_sfinae_handler_substitution_rate() {
let mut handler = X86SfinaeHandler::new();
handler.enter("ctx1").unwrap();
handler.record_error("fail1", X86SfinaeSeverity::Soft);
handler.leave();
handler.enter("ctx2").unwrap();
handler.leave();
let rate = handler.substitution_success_rate();
assert!(rate <= 1.0 && rate >= 0.0);
}
#[test]
fn test_sfinae_handler_default() {
let handler = X86SfinaeHandler::default();
assert!(!handler.is_active());
}
#[test]
fn test_two_phase_creation() {
let lookup = X86TwoPhaseLookup::new();
assert!(lookup.is_phase1());
assert!(!lookup.is_phase2());
assert_eq!(lookup.deferred_count(), 0);
}
#[test]
fn test_two_phase_defer_name() {
let mut lookup = X86TwoPhaseLookup::new();
lookup.defer_name(
"dependent_type",
X86DeferredNameKind::DependentType,
10,
5,
Some("vector<T>"),
);
assert_eq!(lookup.deferred_count(), 1);
}
#[test]
fn test_two_phase_resolve_deferred() {
let mut lookup = X86TwoPhaseLookup::new();
lookup.defer_name("T", X86DeferredNameKind::DependentType, 1, 1, None);
let mgr = X86EnhancedScopeManager::new();
let results = lookup.resolve_deferred(&mgr);
assert_eq!(results.len(), 1);
}
#[test]
fn test_two_phase_enter_phase2() {
let mut lookup = X86TwoPhaseLookup::new();
assert!(lookup.is_phase1());
lookup.enter_phase2();
assert!(lookup.is_phase2());
assert!(!lookup.is_phase1());
}
#[test]
fn test_two_phase_clear() {
let mut lookup = X86TwoPhaseLookup::new();
lookup.defer_name("T", X86DeferredNameKind::DependentType, 1, 1, None);
lookup.clear();
assert_eq!(lookup.deferred_count(), 0);
}
#[test]
fn test_deferred_name_kind_display() {
assert_eq!(
X86DeferredNameKind::DependentType.to_string(),
"dependent-type"
);
assert_eq!(
X86DeferredNameKind::DependentValue.to_string(),
"dependent-value"
);
assert_eq!(
X86DeferredNameKind::DependentTemplate.to_string(),
"dependent-template"
);
}
#[test]
fn test_dependent_resolver_creation() {
let resolver = X86DependentNameResolver::new();
assert!(!resolver.is_dependent());
assert_eq!(resolver.dependence_level, 0);
}
#[test]
fn test_dependent_resolver_enter_leave() {
let mut resolver = X86DependentNameResolver::new();
resolver.enter_dependent_context();
assert!(resolver.is_dependent());
resolver.enter_dependent_context();
assert_eq!(resolver.dependence_level, 2);
resolver.leave_dependent_context();
assert_eq!(resolver.dependence_level, 1);
resolver.leave_dependent_context();
assert!(!resolver.is_dependent());
}
#[test]
fn test_dependent_resolver_needs_typename() {
let mut resolver = X86DependentNameResolver::new();
resolver.enter_dependent_context();
resolver.mark_requires_typename("value_type");
assert!(resolver.needs_typename_keyword("value_type"));
assert!(!resolver.needs_typename_keyword("other_type"));
resolver.leave_dependent_context();
}
#[test]
fn test_dependent_resolver_needs_template() {
let mut resolver = X86DependentNameResolver::new();
resolver.enter_dependent_context();
resolver.mark_requires_template("get");
assert!(resolver.needs_template_keyword("get"));
resolver.leave_dependent_context();
}
#[test]
fn test_dependent_resolver_classify_non_dependent() {
let resolver = X86DependentNameResolver::new();
assert_eq!(
resolver.classify_dependence(false, false),
X86FinalDependence::NonDependent
);
}
#[test]
fn test_dependent_resolver_classify_fully_dependent() {
let resolver = X86DependentNameResolver::new();
assert_eq!(
resolver.classify_dependence(true, true),
X86FinalDependence::FullyDependent
);
}
#[test]
fn test_dependent_resolver_default() {
let resolver = X86DependentNameResolver::default();
assert!(!resolver.is_dependent());
}
#[test]
fn test_constexpr_final_int_literal() {
let mut eval = X86FinalConstexprEval::new();
let result = eval.evaluate(&Expr::IntLiteral(42));
assert_eq!(result.unwrap().as_i128(), Some(42));
}
#[test]
fn test_constexpr_final_float_literal() {
let mut eval = X86FinalConstexprEval::new();
let result = eval.evaluate(&Expr::FloatLiteral(3.14));
assert!((result.unwrap().as_f64().unwrap() - 3.14).abs() < 0.001);
}
#[test]
fn test_constexpr_final_binary_add() {
let mut eval = X86FinalConstexprEval::new();
let expr = Expr::Binary(
BinaryOp::Add,
Box::new(Expr::IntLiteral(1)),
Box::new(Expr::IntLiteral(2)),
);
assert_eq!(eval.evaluate(&expr).unwrap().as_i128(), Some(3));
}
#[test]
fn test_constexpr_final_binary_div_zero() {
let mut eval = X86FinalConstexprEval::new();
let expr = Expr::Binary(
BinaryOp::Div,
Box::new(Expr::IntLiteral(1)),
Box::new(Expr::IntLiteral(0)),
);
assert!(eval.evaluate(&expr).is_none());
assert!(eval
.errors
.iter()
.any(|e| matches!(e, X86FinalConstExprError::DivisionByZero)));
}
#[test]
fn test_constexpr_final_binary_comparison() {
let mut eval = X86FinalConstexprEval::new();
let expr = Expr::Binary(
BinaryOp::LT,
Box::new(Expr::IntLiteral(1)),
Box::new(Expr::IntLiteral(2)),
);
let result = eval.evaluate(&expr).unwrap();
assert!(result.as_i128().unwrap() != 0); }
#[test]
fn test_constexpr_final_unary_neg() {
let mut eval = X86FinalConstexprEval::new();
let expr = Expr::Unary(crate::clang::UnaryOp::Neg, Box::new(Expr::IntLiteral(5)));
assert_eq!(eval.evaluate(&expr).unwrap().as_i128(), Some(-5));
}
#[test]
fn test_constexpr_final_unary_logical_not() {
let mut eval = X86FinalConstexprEval::new();
let expr = Expr::Unary(
crate::clang::UnaryOp::LogicalNot,
Box::new(Expr::IntLiteral(0)),
);
let result = eval.evaluate(&expr).unwrap();
assert!(result.as_i128().unwrap() != 0);
}
#[test]
fn test_constexpr_final_bool_literal() {
let mut eval = X86FinalConstexprEval::new();
let result = eval.evaluate(&Expr::IntLiteral(1)); assert_eq!(result.unwrap().as_i128(), Some(1));
}
#[test]
fn test_constexpr_final_nullptr() {
let mut eval = X86FinalConstexprEval::new();
let result = eval.evaluate(&Expr::IntLiteral(0)); assert!(result.unwrap().is_zero());
}
#[test]
fn test_constexpr_final_max_depth() {
let mut eval = X86FinalConstexprEval::new();
eval.max_depth = 3;
assert_eq!(eval.max_depth, 3);
}
#[test]
fn test_constexpr_final_max_steps() {
let mut eval = X86FinalConstexprEval::new();
eval.max_steps = 100;
assert_eq!(eval.max_steps, 100);
}
#[test]
fn test_constexpr_final_enum_value() {
let mut eval = X86FinalConstexprEval::new();
eval.register_enum_value("RED", 0);
eval.register_enum_value("GREEN", 1);
eval.register_enum_value("BLUE", 2);
assert_eq!(eval.enum_values.len(), 3);
}
#[test]
fn test_constexpr_final_constexpr_var() {
let mut eval = X86FinalConstexprEval::new();
eval.register_constexpr_var("N", X86FinalConstValue::Int(100));
let result = eval.evaluate(&Expr::Ident("N".into()));
assert_eq!(result.unwrap().as_i128(), Some(100));
}
#[test]
fn test_constexpr_final_is_constant_true() {
assert!(X86FinalConstValue::Int(42).is_constant());
assert!(X86FinalConstValue::Float(1.0).is_constant());
assert!(X86FinalConstValue::Bool(true).is_constant());
assert!(!X86FinalConstValue::NonConstant.is_constant());
}
#[test]
fn test_constexpr_final_is_zero() {
assert!(X86FinalConstValue::Int(0).is_zero());
assert!(X86FinalConstValue::Float(0.0).is_zero());
assert!(X86FinalConstValue::Bool(false).is_zero());
assert!(!X86FinalConstValue::Int(1).is_zero());
}
#[test]
fn test_constexpr_final_stats() {
let mut eval = X86FinalConstexprEval::new();
eval.register_enum_value("A", 1);
eval.register_constexpr_var("B", X86FinalConstValue::Int(2));
eval.register_constexpr_function("factorial");
let stats = eval.stats();
assert_eq!(stats.enum_count, 1);
assert_eq!(stats.var_count, 1);
assert_eq!(stats.function_count, 1);
}
#[test]
fn test_constexpr_final_display() {
assert_eq!(X86FinalConstValue::Int(42).to_string(), "42");
assert_eq!(X86FinalConstValue::Float(3.14).to_string(), "3.14");
assert_eq!(X86FinalConstValue::Bool(true).to_string(), "true");
assert_eq!(X86FinalConstValue::NullPtr.to_string(), "nullptr");
}
#[test]
fn test_constexpr_final_error_display() {
let err = X86FinalConstExprError::DivisionByZero;
assert!(err.to_string().contains("division by zero"));
}
#[test]
fn test_concept_checker_creation() {
let checker = X86ConceptChecker::new();
assert!(!checker.has_concept("any"));
}
#[test]
fn test_concept_define_and_check() {
let mut checker = X86ConceptChecker::new();
checker.define_concept("integral", vec!["T".into()], "true");
assert!(checker.has_concept("integral"));
let result = checker.check_satisfaction("integral", &["int".into()]);
assert!(result.is_satisfied());
}
#[test]
fn test_concept_hard_false() {
let mut checker = X86ConceptChecker::new();
checker.define_concept("always_false", vec!["T".into()], "false");
let result = checker.check_satisfaction("always_false", &["int".into()]);
assert!(!result.is_satisfied());
}
#[test]
fn test_concept_unknown() {
let mut checker = X86ConceptChecker::new();
let result = checker.check_satisfaction("nonexistent", &["int".into()]);
assert!(!result.is_satisfied());
}
#[test]
fn test_concept_wrong_arg_count() {
let mut checker = X86ConceptChecker::new();
checker.define_concept("my_concept", vec!["T".into()], "true");
let result = checker.check_satisfaction("my_concept", &["int".into(), "float".into()]);
assert!(!result.is_satisfied());
}
#[test]
fn test_concept_satisfaction_count() {
let mut checker = X86ConceptChecker::new();
checker.define_concept("c1", vec!["T".into()], "true");
checker.define_concept("c2", vec!["T".into()], "false");
checker.check_satisfaction("c1", &["int".into()]);
checker.check_satisfaction("c2", &["int".into()]);
assert_eq!(checker.satisfied_count(), 1);
assert_eq!(checker.violated_count(), 1);
}
#[test]
fn test_concept_requires_clause() {
let mut checker = X86ConceptChecker::new();
checker.define_concept("integral", vec!["T".into()], "true");
let result = checker.requires_clause_check("integral<int>");
assert!(result.is_satisfied());
}
#[test]
fn test_concept_empty_requires() {
let mut checker = X86ConceptChecker::new();
let result = checker.requires_clause_check("");
assert!(result.is_satisfied());
}
#[test]
fn test_concept_report() {
let mut checker = X86ConceptChecker::new();
checker.define_concept("c1", vec!["T".into()], "true");
checker.check_satisfaction("c1", &["int".into()]);
let report = checker.report();
assert!(report.contains("Satisfied: 1"));
}
#[test]
fn test_concept_cache() {
let mut checker = X86ConceptChecker::new();
checker.define_concept("c1", vec!["T".into()], "true");
checker.check_satisfaction("c1", &["int".into()]);
checker.clear_cache();
}
#[test]
fn test_concept_default() {
let checker = X86ConceptChecker::default();
assert!(!checker.has_concept("any"));
}
#[test]
fn test_deep_sema_final_creation() {
let sema = make_sema();
assert_eq!(sema.standard, "c17");
assert!(sema.is_64bit);
assert!(sema.has_no_errors());
}
#[test]
fn test_deep_sema_final_creation_32() {
let sema = make_sema_32();
assert!(!sema.is_64bit);
assert!(sema.target.contains("i386"));
}
#[test]
fn test_deep_sema_final_creation_cxx() {
let sema = make_sema_cxx();
assert_eq!(sema.standard, "c++17");
}
#[test]
fn test_deep_sema_final_analyze_empty_tu() {
let mut sema = make_sema();
let tu = make_simple_tu();
let ok = sema.analyze_tu(&tu);
assert!(ok);
}
#[test]
fn test_deep_sema_final_error_count() {
let mut sema = make_sema();
sema.errors.push("test error".into());
assert_eq!(sema.error_count(), 1);
assert!(sema.has_errors());
assert!(!sema.has_no_errors());
}
#[test]
fn test_deep_sema_final_report() {
let mut sema = make_sema();
let report = sema.report();
assert!(report.contains("X86DeepSemaFinal Report"));
assert!(report.contains("x86_64"));
}
#[test]
fn test_deep_sema_final_template_depth() {
let mut sema = make_sema();
let result = sema.enter_template_instantiation(
"vector<int>",
vec!["int".into()],
10,
5,
X86InstantiationKind::Explicit,
);
assert!(result.is_ok());
assert_eq!(sema.depth_tracker.current_depth, 1);
sema.leave_template_instantiation();
assert_eq!(sema.depth_tracker.current_depth, 0);
}
#[test]
fn test_deep_sema_final_sfinae() {
let mut sema = make_sema();
let result = sema.enter_sfinae("test_substitution");
assert!(result.is_ok());
assert!(sema.is_sfinae_active());
sema.leave_sfinae();
assert!(!sema.is_sfinae_active());
}
#[test]
fn test_deep_sema_final_default() {
let sema = X86DeepSemaFinal::default();
assert_eq!(sema.standard, "c17");
assert!(sema.is_64bit);
}
#[test]
fn test_make_x86_64_deep_sema_final() {
let sema = make_x86_64_deep_sema_final();
assert!(sema.is_64bit);
}
#[test]
fn test_make_x86_32_deep_sema_final() {
let sema = make_x86_32_deep_sema_final();
assert!(!sema.is_64bit);
}
#[test]
fn test_make_x86_64_cxx17_deep_sema_final() {
let sema = make_x86_64_cxx17_deep_sema_final();
assert_eq!(sema.standard, "c++17");
}
#[test]
fn test_analyze_x86_tu_final() {
let tu = make_simple_tu();
let (ok, errors, warnings) = analyze_x86_tu_final(&tu);
assert!(ok);
assert!(errors.is_empty());
assert!(warnings.is_empty());
}
#[test]
fn test_x86_depth_stats() {
let tracker = X86DepthTracker::default();
let stats = x86_depth_stats(&tracker);
assert_eq!(stats.current_depth, 0);
}
#[test]
fn test_x86_eval_constexpr_final() {
let expr = Expr::IntLiteral(42);
let result = x86_eval_constexpr_final(&expr);
assert_eq!(result.unwrap().as_i128(), Some(42));
}
#[test]
fn prop_enhanced_scope_enter_leave_balanced() {
for _ in 0..100 {
let mut mgr = X86EnhancedScopeManager::new();
mgr.enter_scope(X86EnhancedScopeKind::FunctionBody);
mgr.enter_scope(X86EnhancedScopeKind::Block);
mgr.enter_scope(X86EnhancedScopeKind::Lambda);
mgr.leave_scope();
mgr.leave_scope();
mgr.leave_scope();
assert_eq!(mgr.scope_depth(), 0);
}
}
#[test]
fn prop_depth_tracker_never_below_zero() {
let mut tracker = X86DepthTracker::new(100);
tracker
.enter("T", vec![], 1, 1, X86InstantiationKind::Implicit)
.unwrap();
tracker.leave();
tracker.leave(); tracker.leave();
assert_eq!(tracker.current_depth, 0);
}
#[test]
fn prop_constexpr_int_add_commutative() {
let mut eval = X86FinalConstexprEval::new();
let a = Expr::IntLiteral(3);
let b = Expr::IntLiteral(5);
let expr1 = Expr::Binary(BinaryOp::Add, Box::new(a.clone()), Box::new(b.clone()));
let expr2 = Expr::Binary(BinaryOp::Add, Box::new(b), Box::new(a));
let v1 = eval.evaluate(&expr1).unwrap().as_i128();
let v2 = eval.evaluate(&expr2).unwrap().as_i128();
assert_eq!(v1, v2);
}
#[test]
fn stress_many_scope_enter_leave() {
let mut mgr = X86EnhancedScopeManager::new();
for _ in 0..1000 {
mgr.enter_scope(X86EnhancedScopeKind::Block);
}
assert_eq!(mgr.scope_depth(), 1000);
for _ in 0..1000 {
mgr.leave_scope();
}
assert_eq!(mgr.scope_depth(), 0);
}
#[test]
fn stress_many_depth_entries() {
let mut tracker = X86DepthTracker::new(10000);
for i in 0..500 {
let name = format!("Template{}", i);
tracker
.enter(
&name,
vec!["int".into()],
i as usize,
1,
X86InstantiationKind::Implicit,
)
.unwrap();
}
assert_eq!(tracker.current_depth, 500);
assert_eq!(tracker.total_instantiations, 500);
}
#[test]
fn stress_constexpr_many_operations() {
let mut eval = X86FinalConstexprEval::new();
eval.max_steps = 100000;
let mut expr = Expr::IntLiteral(1);
for _ in 0..100 {
expr = Expr::Binary(BinaryOp::Add, Box::new(expr), Box::new(Expr::IntLiteral(1)));
}
let result = eval.evaluate(&expr);
assert!(result.is_some());
assert!(eval.steps_consumed() > 0);
}
#[test]
fn stress_many_concept_checks() {
let mut checker = X86ConceptChecker::new();
for i in 0..200 {
checker.define_concept(&format!("concept_{}", i), vec!["T".into()], "true");
}
for i in 0..200 {
let result = checker.check_satisfaction(&format!("concept_{}", i), &["int".into()]);
assert!(result.is_satisfied());
}
assert_eq!(checker.satisfied_count(), 200);
}
#[test]
fn stress_all_components_together() {
let mut sema = make_sema_cxx();
sema.enter_template_instantiation(
"std::array<int, 5>",
vec!["int".into(), "5".into()],
1,
1,
X86InstantiationKind::Explicit,
)
.unwrap();
sema.enter_sfinae("test_sub").unwrap();
let result = sema.lookup_name_phase1("unused");
assert!(matches!(result, X86TwoPhaseResult::NotFound));
sema.leave_sfinae();
sema.concept_checker
.define_concept("integral", vec!["T".into()], "true");
let cresult = sema.check_concept("integral", &["int".into()]);
assert!(cresult.is_satisfied());
sema.leave_template_instantiation();
let report = sema.report();
assert!(!report.is_empty());
}
#[test]
fn smoke_all_enhanced_scope_kinds_display() {
let kinds = [
X86EnhancedScopeKind::FunctionPrototype,
X86EnhancedScopeKind::FunctionBody,
X86EnhancedScopeKind::Block,
X86EnhancedScopeKind::Namespace,
X86EnhancedScopeKind::Class,
X86EnhancedScopeKind::Struct,
X86EnhancedScopeKind::Enum,
X86EnhancedScopeKind::TemplateParam,
X86EnhancedScopeKind::TemplateInstantiation,
X86EnhancedScopeKind::TryBlock,
X86EnhancedScopeKind::CatchBlock,
X86EnhancedScopeKind::Lambda,
X86EnhancedScopeKind::RequiresClause,
X86EnhancedScopeKind::ConstexprEval,
X86EnhancedScopeKind::EnumClass,
X86EnhancedScopeKind::LinkageSpec,
X86EnhancedScopeKind::Friend,
X86EnhancedScopeKind::ExplicitSpecialization,
X86EnhancedScopeKind::ExplicitInstantiation,
];
for kind in &kinds {
let s = kind.to_string();
assert!(!s.is_empty());
}
}
#[test]
fn smoke_all_final_dependence_display() {
let deps = [
X86FinalDependence::NonDependent,
X86FinalDependence::TypeDependent,
X86FinalDependence::ValueDependent,
X86FinalDependence::FullyDependent,
X86FinalDependence::InstantiationDependent,
X86FinalDependence::EnclosingTemplate,
];
for dep in &deps {
let s = dep.to_string();
assert!(!s.is_empty());
}
}
#[test]
fn smoke_all_constexpr_error_display() {
let errors = [
X86FinalConstExprError::DivisionByZero,
X86FinalConstExprError::Overflow,
X86FinalConstExprError::NonConstantOperand,
X86FinalConstExprError::ReadNonConstVariable("x".into()),
X86FinalConstExprError::CallNonConstexprFunction("f".into()),
X86FinalConstExprError::InfiniteRecursion,
X86FinalConstExprError::MaxDepthExceeded(100),
X86FinalConstExprError::MaxStepsExceeded(1000),
X86FinalConstExprError::UndefinedBehavior("test".into()),
X86FinalConstExprError::SideEffect("test".into()),
X86FinalConstExprError::Other("test".into()),
];
for err in &errors {
let s = err.to_string();
assert!(!s.is_empty());
}
}
#[test]
fn smoke_const_value_display_all() {
let vals = [
X86FinalConstValue::Int(42),
X86FinalConstValue::UInt(42),
X86FinalConstValue::Float(3.14),
X86FinalConstValue::Bool(true),
X86FinalConstValue::NullPtr,
X86FinalConstValue::StringLiteral(b"hello".to_vec()),
X86FinalConstValue::Address(0x1000),
X86FinalConstValue::Composite(vec![
X86FinalConstValue::Int(1),
X86FinalConstValue::Int(2),
]),
X86FinalConstValue::NonConstant,
];
for val in &vals {
let s = val.to_string();
assert!(!s.is_empty());
}
}
}