Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
258 changes: 240 additions & 18 deletions src/ast.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ use alloc::vec::Vec;
use core::cell::Cell;
#[cfg(feature = "logging")]
use core::cell::RefCell;
use core::convert::TryFrom;
use core::fmt::{self, Write};
use core::hash::{Hash, Hasher};
use core::mem;
Expand Down Expand Up @@ -351,6 +352,11 @@ trait ArgScope<'me, 'ctx>: fmt::Debug {
/// Get the current scope's leaf name.
fn leaf_name(&'me self) -> Result<LeafName<'ctx>>;

#[inline]
fn template_args(&'me self) -> Option<&'ctx TemplateArgs> {
None
}

/// Get the current scope's `index`th template argument.
fn get_template_arg(&'me self, index: usize)
-> Result<(&'ctx TemplateArg, &'ctx TemplateArgs)>;
Expand Down Expand Up @@ -390,6 +396,12 @@ trait ArgScopeStackExt<'prev, 'subs>: Copy {
&'prev self,
item: &'subs dyn ArgScope<'subs, 'subs>,
) -> Option<ArgScopeStack<'prev, 'subs>>;

fn get_template_arg_by_level(
&'prev self,
level_from_current: Option<usize>,
idx: usize,
) -> Result<(&'subs TemplateArg, &'subs TemplateArgs)>;
}

impl<'prev, 'subs> ArgScopeStackExt<'prev, 'subs> for Option<ArgScopeStack<'prev, 'subs>> {
Expand All @@ -404,6 +416,41 @@ impl<'prev, 'subs> ArgScopeStackExt<'prev, 'subs> for Option<ArgScopeStack<'prev
item: item,
})
}

fn get_template_arg_by_level(
&'prev self,
level_from_current: Option<usize>,
idx: usize,
) -> Result<(&'subs TemplateArg, &'subs TemplateArgs)> {
let target_level = level_from_current.unwrap_or(0);

let mut scope = self.as_ref();
let mut template_level = 0usize;

while let Some(s) = scope {
if s.item.template_args().is_some() {
if template_level == target_level {
if let Ok((arg, args)) = s.item.get_template_arg(idx) {
if let Some((in_idx, in_args)) = s.in_arg {
if core::ptr::eq(args, in_args) && in_idx <= idx {
scope = s.prev;
template_level += 1;
continue;
}
}
return Ok((arg, args));
}
return Err(error::Error::BadTemplateArgReference);
}

template_level += 1;
}

scope = s.prev;
}

Err(error::Error::BadTemplateArgReference)
}
}

/// A stack of `ArgScope`s is itself an `ArgScope`!
Expand All @@ -427,9 +474,7 @@ impl<'prev, 'subs> ArgScope<'prev, 'subs> for Option<ArgScopeStack<'prev, 'subs>
while let Some(s) = scope {
if let Ok((arg, args)) = s.item.get_template_arg(idx) {
if let Some((in_idx, in_args)) = s.in_arg {
if args as *const TemplateArgs == in_args as *const TemplateArgs
&& in_idx <= idx
{
if core::ptr::eq(args, in_args) && in_idx <= idx {
return Err(error::Error::ForwardTemplateArgReference);
}
}
Expand Down Expand Up @@ -552,6 +597,9 @@ where
// We are currently demangling a template-prefix in a nested-name.
is_template_prefix_in_nested_name: bool,

// We are currently demangling a requires-clause expression.
is_requires_clause: bool,

// `PackExpansion`'s should only print '...', only when there is no template
// argument pack.
is_template_argument_pack: bool,
Expand Down Expand Up @@ -617,6 +665,7 @@ where
is_lambda_arg: false,
is_template_prefix: false,
is_template_prefix_in_nested_name: false,
is_requires_clause: false,
is_template_argument_pack: false,
is_explicit_obj_param: false,
show_params: !options.no_params,
Expand Down Expand Up @@ -5362,6 +5411,62 @@ where
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct TemplateParam(usize);

impl TemplateParam {
const EXPLICIT_LEVEL_FLAG: usize = 1usize << (usize::BITS as usize - 1);
const INDEX_BITS: usize = (usize::BITS as usize - 1) / 2;
const INDEX_MASK: usize = (1usize << Self::INDEX_BITS) - 1;
const IMPLICIT_INDEX_MASK: usize = Self::EXPLICIT_LEVEL_FLAG - 1;

#[inline]
fn implicit(index: usize) -> TemplateParam {
TemplateParam(index)
}

#[inline]
fn checked_implicit(index: usize) -> Result<TemplateParam> {
if index > Self::IMPLICIT_INDEX_MASK {
Err(error::Error::Overflow)
} else {
Ok(TemplateParam::implicit(index))
}
}

#[inline]
fn explicit(level_from_current: usize, index: usize) -> TemplateParam {
debug_assert!(level_from_current <= Self::INDEX_MASK);
debug_assert!(index <= Self::INDEX_MASK);
let encoded = Self::EXPLICIT_LEVEL_FLAG | (level_from_current << Self::INDEX_BITS) | index;
TemplateParam(encoded)
}

#[inline]
fn checked_explicit(level_from_current: usize, index: usize) -> Result<TemplateParam> {
if level_from_current > Self::INDEX_MASK || index > Self::INDEX_MASK {
Err(error::Error::Overflow)
} else {
Ok(TemplateParam::explicit(level_from_current, index))
}
}

#[inline]
fn level_from_current(&self) -> Option<usize> {
if (self.0 & Self::EXPLICIT_LEVEL_FLAG) == 0 {
None
} else {
Some((self.0 >> Self::INDEX_BITS) & Self::INDEX_MASK)
}
}

#[inline]
fn index(&self) -> usize {
if (self.0 & Self::EXPLICIT_LEVEL_FLAG) == 0 {
self.0
} else {
self.0 & Self::INDEX_MASK
}
}
}

impl Parse for TemplateParam {
fn parse<'a, 'b>(
ctx: &'a ParseContext,
Expand All @@ -5371,12 +5476,41 @@ impl Parse for TemplateParam {
try_begin_parse!("TemplateParam", ctx, input);

let input = consume(b"T", input)?;

if let Ok(input) = consume(b"L", input) {
// C++20 can qualify template parameters with an explicit template
// nesting level (`TL<level>_..._`). Level 0 means current
// (innermost) template parameter scope.
let (level, input) = parse_number(10, false, input)?;
let level = usize::try_from(level).map_err(|_| error::Error::Overflow)?;
let input = consume(b"_", input)?;
let (number, input) = match parse_number(10, false, input) {
Ok((number, input)) => {
let number = usize::try_from(number).map_err(|_| error::Error::Overflow)?;
let number = number.checked_add(1).ok_or(error::Error::Overflow)?;
(number, input)
}
Err(error::Error::UnexpectedText) => (0, input),
Err(err) => return Err(err),
};
let input = consume(b"_", input)?;
if level == 0 {
return Ok((TemplateParam::checked_implicit(number)?, input));
}
return Ok((TemplateParam::checked_explicit(level, number)?, input));
}

let (number, input) = match parse_number(10, false, input) {
Ok((number, input)) => ((number + 1) as _, input),
Err(_) => (0, input),
Ok((number, input)) => {
let number = usize::try_from(number).map_err(|_| error::Error::Overflow)?;
let number = number.checked_add(1).ok_or(error::Error::Overflow)?;
(number, input)
}
Err(error::Error::UnexpectedText) => (0, input),
Err(err) => return Err(err),
};
let input = consume(b"_", input)?;
Ok((TemplateParam(number), input))
Ok((TemplateParam::checked_implicit(number)?, input))
}
}

Expand All @@ -5394,7 +5528,10 @@ where
ctx.push_demangle_node(DemangleNodeType::TemplateParam);
let ret = if ctx.is_lambda_arg {
// To match libiberty, template references are converted to `auto`.
write!(ctx, "auto:{}", self.0 + 1)
write!(ctx, "auto:{}", self.index() + 1)
} else if ctx.is_requires_clause {
// Keep symbolic template parameters inside requires clauses.
write!(ctx, "T{}", self.index() + 1)
} else {
let arg = self.resolve(scope)?;
arg.demangle(ctx, scope)
Expand All @@ -5409,8 +5546,14 @@ impl TemplateParam {
&'subs self,
scope: Option<ArgScopeStack<'prev, 'subs>>,
) -> ::core::result::Result<&'subs TemplateArg, fmt::Error> {
scope
.get_template_arg(self.0)
let resolved = if let Some(level_from_current) = self.level_from_current() {
scope.get_template_arg_by_level(Some(level_from_current), self.index())
} else {
// Preserve legacy semantics for implicit template params.
scope.get_template_arg(self.index())
};

resolved
.map_err(|e| {
log!("Error obtaining template argument: {}", e);
fmt::Error
Expand Down Expand Up @@ -5681,6 +5824,8 @@ where
scope.in_arg = None;
}

self.requires_clause.demangle(ctx, scope)?;

// Ensure "> >" because old C++ sucks and libiberty (and its tests)
// supports old C++.
if ctx.last_char_written == Some('>') {
Expand All @@ -5697,6 +5842,10 @@ impl<'subs> ArgScope<'subs, 'subs> for TemplateArgs {
Err(error::Error::BadLeafNameReference)
}

fn template_args(&'subs self) -> Option<&'subs TemplateArgs> {
Some(self)
}

fn get_template_arg(
&'subs self,
idx: usize,
Expand Down Expand Up @@ -6784,6 +6933,30 @@ impl Parse for ConstraintExpression {
}
}

impl<'subs, W> Demangle<'subs, W> for ConstraintExpression
where
W: 'subs + DemangleWrite,
{
fn demangle<'prev, 'ctx>(
&'subs self,
ctx: &'ctx mut DemangleContext<'subs, W>,
scope: Option<ArgScopeStack<'prev, 'subs>>,
) -> fmt::Result {
let ctx = try_begin_demangle!(self, ctx, scope);

if let Some(expr) = self.0.as_ref() {
write!(ctx, " requires ")?;
let saved = ctx.is_requires_clause;
ctx.is_requires_clause = true;
let ret = expr.demangle(ctx, scope);
ctx.is_requires_clause = saved;
return ret;
}

Ok(())
}
}

/// The `<unresolved-name>` production.
///
/// ```text
Expand Down Expand Up @@ -8585,22 +8758,23 @@ mod tests {
use super::{
AbiTag, AbiTags, ArrayType, BareFunctionType, BaseUnresolvedName, BuiltinType, CallOffset,
ClassEnumType, ClosureTypeName, ConstraintExpression, CtorDtorName, CvQualifiers,
DataMemberPrefix, Decltype, DestructorName, Discriminator, Encoding, ExceptionSpec,
ExprPrimary, Expression, FoldExpr, FunctionParam, FunctionType, GlobalCtorDtor, Identifier,
Initializer, LambdaSig, LocalName, MangledName, MemberName, Name, NestedName,
NonSubstitution, Number, NvOffset, OperatorName, ParametricBuiltinType, Parse,
ParseContext, PointerToMemberType, Prefix, PrefixHandle, RefQualifier, ResourceName, SeqId,
SimpleId, SimpleOperatorName, SourceName, SpecialName, StandardBuiltinType, SubobjectExpr,
Substitution, TemplateArg, TemplateArgs, TemplateParam, TemplateParamDecl,
TemplateTemplateParam, TemplateTemplateParamHandle, Type, TypeHandle, UnnamedTypeName,
UnqualifiedName, UnresolvedName, UnresolvedQualifierLevel, UnresolvedType,
DataMemberPrefix, Decltype, Demangle, DemangleContext, DemangleOptions, DestructorName,
Discriminator, Encoding, ExceptionSpec, ExprPrimary, Expression, FoldExpr, FunctionParam,
FunctionType, GlobalCtorDtor, Identifier, Initializer, LambdaSig, LocalName, MangledName,
MemberName, Name, NestedName, NonSubstitution, Number, NvOffset, OperatorName,
ParametricBuiltinType, Parse, ParseContext, PointerToMemberType, Prefix, PrefixHandle,
RefQualifier, ResourceName, SeqId, SimpleId, SimpleOperatorName, SourceName, SpecialName,
StandardBuiltinType, SubobjectExpr, Substitution, TemplateArg, TemplateArgs, TemplateParam,
TemplateParamDecl, TemplateTemplateParam, TemplateTemplateParamHandle, Type, TypeHandle,
UnnamedTypeName, UnqualifiedName, UnresolvedName, UnresolvedQualifierLevel, UnresolvedType,
UnresolvedTypeHandle, UnscopedName, UnscopedTemplateName, UnscopedTemplateNameHandle,
VOffset, VectorType, WellKnownComponent,
};

use crate::error::Error;
use crate::index_str::IndexStr;
use crate::subs::{Substitutable, SubstitutionTable};
use crate::Symbol;
use alloc::boxed::Box;
use alloc::string::String;
use core::fmt::Debug;
Expand Down Expand Up @@ -10156,6 +10330,44 @@ mod tests {
});
}

#[test]
fn demangle_template_args_renders_requires_clause() {
let parse_ctx = ParseContext::new(Default::default());
let mut subs = SubstitutionTable::new();
let input = b"IiQtrE";
let (template_args, tail) =
TemplateArgs::parse(&parse_ctx, &mut subs, IndexStr::new(input))
.expect("template args with requires-clause should parse");
assert!(tail.is_empty(), "unexpected parse tail");

let mut out = String::new();
let mut demangle_ctx =
DemangleContext::new(&subs, input, DemangleOptions::default(), &mut out);
template_args
.demangle(&mut demangle_ctx, None)
.expect("requires-clause demangle should succeed");

assert!(
out.contains("requires"),
"demangled template args should render requires clause: {}",
out
);
}

#[test]
fn demangle_realworld_tfunction_requires_clause_probe() {
let mangled = b"_ZN9TFunctionIFvR21FMassExecutionContextEEC2IZN22UMassTestProcessorBaseC1EvE3$_0Qaantntaantsr12TIsTFunctionINSt3__15decayITL0__E4typeEEE5Valuesr3stdE16is_invocable_r_vIT_SB_DpT0_Esr2UE4Core7PrivateE19BoolIdentityConceptILb1EEEEOSC_";
let sym = Symbol::new(&mangled[..]).expect("symbol parse");
let demangled = sym
.demangle()
.expect("requires-clause demangle should succeed");
assert!(
demangled.contains("requires"),
"expected requires clause in demangled output: {}",
demangled
);
}

#[test]
fn parse_template_arg() {
assert_parse!(TemplateArg {
Expand Down Expand Up @@ -11991,11 +12203,21 @@ mod tests {
TemplateParam(4),
b"..."
}
b"TL0__..." => {
TemplateParam(0),
b"..."
}
b"TL1_3_..." => {
TemplateParam::explicit(1, 4),
b"..."
}
}
Err => {
b"wtf" => Error::UnexpectedText,
b"Twtf" => Error::UnexpectedText,
b"T3wtf" => Error::UnexpectedText,
b"TL2147483648_0_..." => Error::Overflow,
b"T9223372036854775807_..." => Error::Overflow,
b"T" => Error::UnexpectedEnd,
b"T3" => Error::UnexpectedEnd,
b"" => Error::UnexpectedEnd,
Expand Down