use super::*;
use crate::{
BlockArgument, Builder, EntityWithId, Forward, FunctionType, FxHashSet, InsertionGuard, Op,
PendingSuccessorInfo, RawWalk, Value, WalkResult,
adt::{SmallDenseMap, SmallSet},
dialects::builtin::{
UnrealizedConversionCast, WorldRef,
attributes::{Location, LocationAttr},
},
traits::{IsolatedFromAbove, Terminator},
};
pub struct OperationParser<P> {
parser: P,
top_level: WorldRef,
isolated_name_scopes: SmallVec<[IsolatedSSANameScope; 2]>,
blocks_by_name: SmallVec<[SmallDenseMap<BlockId, Span<BlockRef>>; 2]>,
forward_ref: SmallVec<[SmallDenseMap<BlockRef, SourceSpan>; 2]>,
forward_ref_placeholders: SmallDenseMap<ValueRef, SourceSpan>,
forward_ref_ops: SmallSet<OperationRef, 2>,
deferred_locs_references: Vec<DeferredLocInfo>,
}
impl<'input, P> Parser<'input> for OperationParser<P>
where
P: Parser<'input>,
{
#[inline(always)]
fn builder(&self) -> &OpBuilder {
self.parser.builder()
}
#[inline(always)]
fn builder_mut(&mut self) -> &mut OpBuilder {
self.parser.builder_mut()
}
#[inline(always)]
fn state(&self) -> &ParserState<'input> {
self.parser.state()
}
#[inline(always)]
fn state_mut(&mut self) -> &mut ParserState<'input> {
self.parser.state_mut()
}
#[inline(always)]
fn token_stream(&self) -> &TokenStream<'input> {
self.parser.token_stream()
}
#[inline(always)]
fn token_stream_mut(&mut self) -> &mut TokenStream<'input> {
self.parser.token_stream_mut()
}
#[inline]
fn parse_extended_attribute(&mut self, ty: &Type) -> ParseResult<Span<AttributeRef>> {
super::parser::parse_extended_attribute(self, ty)
}
}
#[derive(Debug, Copy, Clone)]
pub struct DeferredLocInfo {
loc: SourceSpan,
identifier: interner::Symbol,
}
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub enum OpOrArgument {
Op(OperationRef),
Arg(BlockArgumentRef),
}
impl OpOrArgument {
pub fn span(&self) -> SourceSpan {
use crate::diagnostics::Spanned;
match self {
Self::Op(op) => op.borrow().span,
Self::Arg(arg) => arg.borrow().span(),
}
}
pub fn set_span(self, span: SourceSpan) {
match self {
Self::Op(mut op) => op.borrow_mut().set_span(span),
Self::Arg(mut arg) => arg.borrow_mut().set_span(span),
}
}
}
#[derive(Debug, Copy, Clone)]
pub struct ResultRecord {
pub loc: SourceSpan,
pub id: interner::Symbol,
pub count: u8,
}
#[derive(Default)]
struct IsolatedSSANameScope {
values: FxHashMap<ValueId, SmallVec<[Option<Span<ValueRef>>; 1]>>,
definitions_per_scope: SmallVec<[FxHashSet<ValueId>; 2]>,
}
impl IsolatedSSANameScope {
pub fn record_definition(&mut self, def: ValueId) {
self.definitions_per_scope.last_mut().unwrap().insert(def);
}
pub fn push_ssa_name_scope(&mut self) {
self.definitions_per_scope.push(Default::default());
}
pub fn pop_ssa_name_scope(&mut self) {
for def in self.definitions_per_scope.pop().unwrap() {
self.values.remove(&def);
}
}
}
impl<'input, P> OperationParser<P>
where
P: Parser<'input>,
{
pub fn new(mut parser: P, top_level: WorldRef) -> Self {
{
let world_body = { top_level.borrow().body().as_region_ref() };
if world_body.borrow().is_empty() {
parser.builder_mut().create_block(world_body, None, &[]);
} else {
parser
.builder_mut()
.set_insertion_point_to_end(world_body.borrow().entry_block_ref().unwrap());
}
}
let mut this = Self {
parser,
top_level,
isolated_name_scopes: Default::default(),
blocks_by_name: Default::default(),
forward_ref: Default::default(),
forward_ref_placeholders: Default::default(),
forward_ref_ops: Default::default(),
deferred_locs_references: Default::default(),
};
this.push_ssa_name_scope(true);
if let Some(state) = this.parser.state_mut().asm_state.as_deref_mut() {
state.initialize(top_level.as_operation_ref());
}
this
}
pub fn finalize(mut self) -> ParseResult {
if !self.forward_ref_placeholders.is_empty() {
let mut labels = Vec::with_capacity(self.forward_ref_placeholders.len());
for (_, span) in self.forward_ref_placeholders.iter() {
labels.push(LabeledSpan::new_with_span(None, *span));
}
return Err(ParserError::UndeclaredValueUses { labels });
}
let attribute_aliases = &self.parser.state().symbols.attribute_alias_definitions;
let resolve_location = |op_or_argument: OpOrArgument| {
let fwd_loc = op_or_argument.span();
let Some(loc_index) = Location::is_deferred(fwd_loc) else {
return Ok(());
};
let loc_info = self.deferred_locs_references[loc_index];
let Some(attr) = attribute_aliases.get(&loc_info.identifier) else {
return Err(ParserError::UnresolvedLocationAlias { span: loc_info.loc });
};
let Some(loc_attr) = attr.try_downcast_attr::<LocationAttr>().ok() else {
return Err(ParserError::InvalidLocationAlias {
span: loc_info.loc,
reason: format!("expected location, but found '{:?}'", &attr.borrow()),
});
};
let loc = loc_attr.borrow().as_value().try_into_span(self.context());
op_or_argument.set_span(loc.unwrap_or(SourceSpan::UNKNOWN));
Ok(())
};
let walk_result =
self.top_level
.as_operation_ref()
.raw_prewalk::<Forward, _, _>(|op: OperationRef| {
if let Err(err) = resolve_location(OpOrArgument::Op(op)) {
return WalkResult::Break(err);
}
let op = op.borrow();
for region in op.regions() {
for block in region.body() {
for arg in block.arguments() {
if let Err(err) = resolve_location(OpOrArgument::Arg(*arg)) {
return WalkResult::Break(err);
}
}
}
}
WalkResult::Continue(())
});
if let WalkResult::Break(err) = walk_result {
return Err(err);
}
self.pop_ssa_name_scope()?;
if self.parser.state().config.should_verify_after_parse() {
self.top_level.borrow().as_operation().recursively_verify()?;
}
if let Some(asm_state) = self.parser.state_mut().asm_state.as_deref_mut() {
asm_state.finalize(self.top_level.as_operation_ref());
}
Ok(())
}
}
impl<'input, P> OperationParser<P>
where
P: Parser<'input>,
{
fn push_ssa_name_scope(&mut self, isolated: bool) {
self.blocks_by_name.push(Default::default());
self.forward_ref.push(Default::default());
if isolated {
self.isolated_name_scopes.push(Default::default());
}
self.isolated_name_scopes.last_mut().unwrap().push_ssa_name_scope();
}
fn pop_ssa_name_scope(&mut self) -> ParseResult {
let forward_ref_current_scope = self.forward_ref.pop().unwrap();
if !forward_ref_current_scope.is_empty() {
let mut labels = Vec::default();
for (block, span) in forward_ref_current_scope {
labels.push(LabeledSpan::new_with_span(None, span));
self.top_level.borrow_mut().body_mut().body_mut().push_back(block);
}
return Err(ParserError::UndefinedBlocks { labels });
}
let current_name_scope = self.isolated_name_scopes.last_mut().unwrap();
if current_name_scope.definitions_per_scope.len() == 1 {
self.isolated_name_scopes.pop();
} else {
current_name_scope.pop_ssa_name_scope();
}
self.blocks_by_name.pop();
Ok(())
}
fn add_definition(&mut self, use_info: UnresolvedOperand, value: ValueRef) -> ParseResult {
let entries = self.get_ssa_value_entry(use_info.name);
let result_index = use_info.name.result_index().unwrap_or(0) as usize;
if entries.len() <= result_index {
entries.resize(result_index + 1, None);
}
if let Some(mut existing) = entries[result_index] {
if !self.forward_ref_placeholders.contains_key(existing.inner()) {
return Err(ParserError::ValueRedefinition {
span: use_info.loc,
prev_span: existing.span(),
});
}
if existing.borrow().ty() != value.borrow().ty() {
return Err(ParserError::ValueDefinitionTypeMismatch {
span: use_info.loc,
prev_span: existing.span(),
ty: value.borrow().ty().clone(),
prev_ty: existing.borrow().ty().clone(),
});
}
existing.borrow_mut().replace_all_uses_with(value);
self.forward_ref_placeholders.remove(&existing);
if let Some(asm_state) = self.parser.state_mut().asm_state.as_deref_mut() {
asm_state.refine_definition(existing.into_inner(), value);
}
}
let entries = self.get_ssa_value_entry(use_info.name);
entries[result_index] = Some(Span::new(use_info.loc, value));
self.record_definition(use_info.name);
Ok(())
}
fn parse_optional_ssa_use_list<const N: usize>(
&mut self,
results: &mut SmallVec<[UnresolvedOperand; N]>,
) -> ParseResult {
if !self
.parser
.token_stream_mut()
.is_next(|tok| matches!(tok, Token::PercentIdent(_)))
{
return Ok(());
}
self.parse_comma_separated_list(Delimiter::None, Some("SSA use list"), |parser| {
let result = parser.parse_ssa_use( true)?;
results.push(result);
Ok(true)
})
}
fn parse_ssa_use(&mut self, allow_result_number: bool) -> ParseResult<UnresolvedOperand> {
let (span, id) = self
.parser
.token_stream_mut()
.expect_map("SSA value", |tok| match tok {
Token::PercentIdent(id) => Some(ValueId::from_symbol(interner::Symbol::intern(id))),
_ => None,
})?
.into_parts();
let result_num = self.parser.token_stream_mut().next_if_map(|tok| match tok {
Token::HashIdent(num) => Some(num),
_ => None,
})?;
if let Some(result_num) = result_num {
let (result_num_span, result_num) = result_num.into_parts();
if !allow_result_number {
return Err(ParserError::ResultNumberUsedInArgumentList {
span: result_num_span,
value_span: span,
});
}
let index =
result_num.parse::<u8>().map_err(|err| ParserError::InvalidResultIndex {
span: result_num_span,
value_span: span,
reason: err.to_string(),
})?;
Ok(UnresolvedOperand {
loc: span,
name: id.with_result_index(index),
})
} else {
Ok(UnresolvedOperand {
loc: span,
name: id,
})
}
}
fn resolve_ssa_use(&mut self, use_info: UnresolvedOperand, ty: Type) -> ParseResult<ValueRef> {
let entries = self.get_ssa_value_entry(use_info.name);
let result_index = use_info.name.result_index().unwrap_or(0) as usize;
if result_index < entries.len()
&& let Some(value_ref) = entries[result_index]
{
let (span, value_ref) = value_ref.into_parts();
let value = value_ref.borrow();
let prev_ty = value.ty();
if prev_ty == &ty || matches!(ty, Type::Unknown) {
if let Some(asm_state) = self.parser.state_mut().asm_state.as_deref_mut() {
asm_state.add_uses(value_ref, &[use_info.loc]);
}
return Ok(value_ref);
}
return Err(ParserError::ValueUseTypeMismatch {
span: use_info.loc,
prev_span: span,
ty,
prev_ty: prev_ty.clone(),
});
}
if entries.len() <= result_index {
entries.resize(result_index + 1, None);
}
if entries[0].is_some_and(|v| !self.is_forward_ref_placeholder(v.into_inner())) {
return Err(ParserError::InvalidResultIndex {
span: use_info.loc,
value_span: SourceSpan::UNKNOWN,
reason: format!("{} has index {result_index}", &use_info.name),
});
}
let result = self.create_forward_ref_placeholder(use_info.loc, ty);
let entries = self.get_ssa_value_entry(use_info.name);
entries[result_index] = Some(Span::new(use_info.loc, result));
if let Some(asm_state) = self.parser.state_mut().asm_state.as_deref_mut() {
asm_state.add_uses(result, &[use_info.loc]);
}
Ok(result)
}
fn parse_ssa_def_or_use_and_type<F>(&mut self, mut action: F) -> ParseResult
where
F: FnMut(&mut Self, UnresolvedOperand, Type) -> ParseResult,
{
let use_info = self.parse_ssa_use(true)?;
let ty = self.parser.parse_colon_type()?;
action(self, use_info, ty.into_inner())
}
fn parse_optional_ssa_use_and_type_list<const N: usize>(
&mut self,
results: &mut SmallVec<[ValueRef; N]>,
) -> ParseResult {
let mut value_ids = SmallVec::<[UnresolvedOperand; 4]>::new_const();
self.parse_optional_ssa_use_list(&mut value_ids)?;
if value_ids.is_empty() {
return Ok(());
}
let mut types = SmallVec::<[Type; 4]>::new_const();
self.parser.token_stream_mut().expect(Token::Comma)?;
self.parser.parse_type_list_no_parens(&mut types)?;
if value_ids.len() != types.len() {
let start = value_ids[0].loc;
let end = self.parser.token_stream().current_position();
return Err(ParserError::MismatchedValueAndTypeLists {
span: SourceSpan::new(start.source_id(), start.start()..end),
num_values: value_ids.len(),
num_types: types.len(),
});
}
results.reserve(value_ids.len());
for (unresolved, ty) in value_ids.into_iter().zip(types) {
results.push(self.resolve_ssa_use(unresolved, ty)?);
}
Ok(())
}
fn get_reference_loc(&mut self, id: ValueId) -> Option<SourceSpan> {
let values = &self.isolated_name_scopes.last().unwrap().values;
let entry = values.get(&id.without_result_index())?;
let result_index = id.result_index().unwrap_or(0) as usize;
entry.get(result_index).and_then(|v| v.map(|v| v.span()))
}
fn record_definition(&mut self, id: ValueId) {
self.isolated_name_scopes.last_mut().unwrap().record_definition(id);
}
fn get_ssa_value_entry(&mut self, id: ValueId) -> &mut SmallVec<[Option<Span<ValueRef>>; 1]> {
self.isolated_name_scopes
.last_mut()
.unwrap()
.values
.entry(id.without_result_index())
.or_default()
}
fn create_forward_ref_placeholder(&mut self, loc: SourceSpan, ty: Type) -> ValueRef {
let name = self.parser.context().get_registered_name::<UnrealizedConversionCast>();
let mut op_ref = UnrealizedConversionCast::alloc_default(self.parser.context_rc());
let result = {
let mut op = op_ref.borrow_mut();
op.set_span(loc);
op.set_ty(ty.clone());
let mut op = op.as_operation_mut();
let result = op.context().make_result(loc, ty, op.as_operation_ref(), 0);
op.results_mut().group_mut(0).push(result);
result as ValueRef
};
self.forward_ref_placeholders.insert(result, loc);
self.forward_ref_ops.insert(op_ref.as_operation_ref());
result
}
}
impl<'input, P> OperationParser<P>
where
P: Parser<'input>,
{
pub fn parse_operation(&mut self) -> ParseResult<OperationRef> {
let start = self.parser.token_stream().current_position();
let mut result_ids = SmallVec::<[ResultRecord; 1]>::new_const();
let mut num_expected_results = 0;
if self
.parser
.token_stream_mut()
.is_next(|tok| matches!(tok, Token::PercentIdent(_)))
{
self.parse_comma_separated_list(Delimiter::None, Some("ssa identifiers"), |parser| {
let (name_span, name) = parser
.token_stream_mut()
.expect_map("SSA identifier", |tok| match tok {
Token::PercentIdent(id) => Some(id),
_ => None,
})?
.into_parts();
let mut expected_sub_results = 1;
let mut end = name_span.end();
if parser.token_stream_mut().next_if_eq(Token::Colon)? {
let (count_span, count_str) = parser
.token_stream_mut()
.expect_map("integer number of results", |tok| match tok {
Token::Int(n) => Some(n),
_ => None,
})?
.into_parts();
let count = count_str.parse::<u8>().map_err(|err| {
ParserError::InvalidIntegerLiteral {
span: count_span,
reason: err.to_string(),
}
})?;
if count == 0 {
return Err(ParserError::InvalidResultCount { span: count_span });
}
end = count_span.end();
expected_sub_results = count;
}
let span = SourceSpan::new(name_span.source_id(), name_span.start()..end);
result_ids.push(ResultRecord {
loc: span,
id: interner::Symbol::intern(name),
count: expected_sub_results,
});
num_expected_results += expected_sub_results;
Ok(true)
})?;
self.parser.token_stream_mut().expect(Token::Equal)?;
}
let source_id = self.source_id();
let Some(name_token) = self
.parser
.token_stream_mut()
.peek()?
.map(|(start, tok, end)| spanned!(source_id, start, end, tok))
else {
return Err(ParserError::UnexpectedEof {
expected: vec!["operation name".to_string()],
});
};
let (name_span, name_token) = name_token.into_parts();
let op = match name_token {
Token::BareIdent(_) => self.parse_custom_operation(&result_ids)?,
Token::String(_) => self.parse_generic_operation(None)?,
invalid => {
return Err(ParserError::UnexpectedToken {
span: name_span,
token: invalid.to_string(),
expected: Some("operation name".to_string()),
});
}
};
let end = self.current_location();
if !result_ids.is_empty() {
let op = op.borrow();
match op.num_results() {
0 => return Err(ParserError::NamedOpWithNoResults { span: name_span }),
n if n != num_expected_results as usize => {
return Err(ParserError::ResultCountMismatch {
span: name_span,
count: n,
expected: num_expected_results,
});
}
_ => (),
}
if let Some(asm_state) = self.state_mut().asm_state.as_deref_mut() {
let mut asm_result_groups = SmallVec::<[_; 4]>::new_const();
asm_result_groups.reserve(result_ids.len());
let mut result_index = 0;
for record in result_ids.iter() {
asm_result_groups.push((result_index as usize, record.loc));
result_index += record.count;
}
asm_state.finalize_operation_definition(
op.as_operation_ref(),
name_span,
end,
&asm_result_groups,
);
}
for (result_group, result_record) in result_ids.iter().enumerate() {
let group = op.results().group(result_group);
for result_index in 0..result_record.count {
let use_info = UnresolvedOperand {
loc: result_record.loc,
name: ValueId::from_symbol(result_record.id)
.with_result_index(result_index),
};
let value = group[result_index as usize] as ValueRef;
self.add_definition(use_info, value);
}
}
} else if let Some(asm_state) = self.state_mut().asm_state.as_deref_mut() {
asm_state.finalize_operation_definition(op, name_span, end, &[]);
}
Ok(op)
}
pub fn parse_successor(&mut self) -> ParseResult<Span<BlockRef>> {
let (span, id) = self
.parser
.token_stream_mut()
.expect_map("block name", |tok| match tok {
Token::CaretIdent(id) => Some(BlockId::from_symbol(interner::Symbol::intern(id))),
_ => None,
})?
.into_parts();
Ok(self.get_block_named(id, span))
}
pub fn parse_successors<const N: usize>(
&mut self,
destinations: &mut SmallVec<[BlockRef; N]>,
) -> ParseResult {
let mut succ_ids = SmallVec::<[Span<BlockId>; N]>::new_const();
self.parse_comma_separated_list(Delimiter::Bracket, Some("successor list"), |parser| {
let id = parser.token_stream_mut().expect_map("block name", |tok| match tok {
Token::CaretIdent(id) => Some(BlockId::from_symbol(interner::Symbol::intern(id))),
_ => None,
})?;
succ_ids.push(id);
Ok(true)
})?;
if destinations.is_empty() {
let source_id = self.source_id();
let at = self.parser.token_stream().current_position();
Err(ParserError::InvalidEmptySuccessorList {
span: SourceSpan::at(source_id, at),
})
} else {
Ok(())
}
}
pub fn parse_generic_operation(
&mut self,
ip: Option<ProgramPoint>,
) -> ParseResult<OperationRef> {
let (span, name) = self
.token_stream_mut()
.expect_map("operation name", |tok| match tok {
Token::String(name) => Some(CompactString::from(name)),
_ => None,
})?
.into_parts();
if name.is_empty() {
return Err(ParserError::InvalidOperationName {
span,
reason: "operation names cannot be empty".to_string(),
});
}
let Some((dialect_name, opcode)) = name.split_once('.') else {
return Err(ParserError::InvalidOperationName {
span,
reason: "operation names must be fully-qualified, e.g. <dialect>.<opcode>"
.to_string(),
});
};
let dialect = self
.parser
.context()
.get_registered_dialect(interner::Symbol::intern(dialect_name));
let opcode = interner::Symbol::intern(opcode);
let Some(op_name) =
dialect.registered_ops().iter().find(|name| name.name() == opcode).cloned()
else {
return Err(ParserError::InvalidOperationName {
span,
reason: "unable to parse unregistered operations".to_string(),
});
};
if let Some(asm_state) = self.state_mut().asm_state.as_deref_mut() {
asm_state.start_operation_definition(&op_name);
}
let mut result = OperationState::new(span, op_name);
let mut guard = CleanupOpStateRegions { state: &mut result };
self.parse_generic_operation_after_name(&mut guard, None, None, None, None, None)?;
let op = if let Some(ip) = ip {
let mut builder = InsertionGuard::new(self.builder_mut());
builder.set_insertion_point(ip);
builder.create_operation(&mut guard)?
} else {
self.builder_mut().create_operation(&mut guard)?
};
self.parse_trailing_location_specifier(OpOrArgument::Op(op))?;
self.parse_semicolon()?;
let end = self.current_location();
if let Some(asm_state) = self.state_mut().asm_state.as_deref_mut() {
asm_state.finalize_operation_definition(op, span, end, &[]);
}
Ok(op)
}
pub fn parse_generic_operation_after_name(
&mut self,
result: &mut OperationState,
parsed_operand_use_info: Option<&[UnresolvedOperand]>,
parsed_successors: Option<&[BlockRef]>,
parsed_regions: Option<&[RegionRef]>,
parsed_attributes: Option<ParsedAttrs>,
parsed_fn_type: Option<FunctionType>,
) -> ParseResult {
let mut operand_info = SmallVec::<[UnresolvedOperand; 8]>::default();
if let Some(provided_use_info) = parsed_operand_use_info {
operand_info.extend_from_slice(provided_use_info);
} else {
self.parser.parse_lparen()?;
self.parse_optional_ssa_use_list(&mut operand_info)?;
self.parser.parse_rparen()?;
}
if !operand_info.is_empty() {
result.operands.push(Default::default());
}
if let Some(provided_succs) = parsed_successors {
result.successors.extend(provided_succs.iter().copied().enumerate().map(
|(i, block)| PendingSuccessorInfo {
block,
key: None,
operand_group: (i + 1) as u8,
},
));
} else if self.parser.token_stream_mut().is_next(|tok| matches!(tok, Token::Lbracket)) {
if !result.name.implements::<dyn Terminator>() {
return Err(ParserError::NonTerminatorWithSuccessors { span: result.span });
}
let mut successors = SmallVec::<[_; 2]>::default();
self.parse_successors(&mut successors)?;
result.successors.extend(successors.into_iter().enumerate().map(|(i, block)| {
PendingSuccessorInfo {
block,
key: None,
operand_group: (i + 1) as u8,
}
}));
}
if let Some(provided_regions) = parsed_regions {
result.regions.extend_from_slice(provided_regions);
} else if self.token_stream_mut().is_next(|tok| matches!(tok, Token::Lparen)) {
loop {
let region = self.builder().context().create_region();
self.parse_region(region, &[], false)?;
result.regions.push(region);
if !self.token_stream_mut().next_if_eq(Token::Comma)? {
break;
}
}
}
if let Some(provided_attrs) = parsed_attributes {
result.attrs.extend(provided_attrs);
} else if self.token_stream_mut().is_next(|tok| matches!(tok, Token::Lbrace)) {
self.parse_attribute_dict(&mut result.attrs)?;
}
let fn_ty = if let Some(provided_fn_ty) = parsed_fn_type {
Span::new(result.span, provided_fn_ty)
} else {
self.parser.parse_colon()?;
self.parser.parse_function_type()?
};
result.results.extend(fn_ty.results().iter().cloned());
if operand_info.len() != fn_ty.arity() {
return Err(ParserError::InvalidOperationType {
span: result.span,
ty_span: fn_ty.span(),
reason: format!(
"expected {} operand type(s), but got {}",
operand_info.len(),
fn_ty.arity()
),
});
}
for (use_info, ty) in operand_info.iter().zip(fn_ty.params()) {
let value = self.resolve_ssa_use(*use_info, ty.clone())?;
result.operands[0].push(value);
}
Ok(())
}
fn parse_trailing_location_specifier(&mut self, op_or_argument: OpOrArgument) -> ParseResult {
if !self.token_stream_mut().next_if_eq(Token::Loc)? {
return Ok(());
}
self.parser.parse_lparen()?;
let loc = if self
.token_stream_mut()
.is_next(|tok| matches!(tok, Token::HashIdent(id) if !id.contains('.')))
{
self.parse_location_alias()?
} else {
self.parser.parse_location_instance()?
};
self.parser.parse_rparen()?;
if let Some(span) = loc.try_into_span(self.context()) {
op_or_argument.set_span(span);
}
Ok(())
}
fn parse_location_alias(&mut self) -> ParseResult<Location> {
let (alias_span, alias) = self
.token_stream_mut()
.expect_map("location alias", |tok| match tok {
Token::HashIdent(id) => Some(id),
_ => None,
})?
.into_parts();
assert!(alias.contains('.'), "unexpected dialect attribute token, expecteed alias");
let alias = interner::Symbol::intern(alias);
if let Some(asm_state) = self.state_mut().asm_state.as_deref_mut() {
asm_state.add_attr_alias_uses(alias, &[alias_span]);
}
if let Some(attr) = self.state_mut().symbols.attribute_alias_definitions.get(&alias) {
if let Ok(loc) = attr.try_downcast_attr::<LocationAttr>() {
Ok(loc.borrow().as_value().clone())
} else {
Err(ParserError::InvalidLocationAlias {
span: alias_span,
reason: format!("expected location, but found '{attr:?}'"),
})
}
} else {
let id = self.deferred_locs_references.len();
self.deferred_locs_references.push(DeferredLocInfo {
loc: alias_span,
identifier: alias,
});
Ok(Location::Opaque(id))
}
}
pub fn parse_custom_operation(
&mut self,
results: &[ResultRecord],
) -> ParseResult<OperationRef> {
let (name_span, name) = self.parse_custom_operation_name()?.into_parts();
let Some(parse_assembly_fn) = name.parse_assembly_fn() else {
return Err(ParserError::InvalidCustomOperation {
span: name_span,
reason: format!("operation '{name}' does not implement OpParser"),
});
};
let isolated_from_above = name.implements::<dyn IsolatedFromAbove>();
let mut op_state = OperationState::new(name_span, name);
if let Some(asm_state) = self.state_mut().asm_state.as_deref_mut() {
asm_state.start_operation_definition(&op_state.name);
}
let span = op_state.span;
let name = op_state.name.clone();
let mut guard = CleanupOpStateRegions {
state: &mut op_state,
};
let mut custom_parser = CustomOpAsmParser::new(
span,
results,
parse_assembly_fn,
name,
isolated_from_above,
self,
);
custom_parser.parse_operation(&mut guard)?;
let op = self.builder_mut().create_operation(&mut guard)?;
self.parse_trailing_location_specifier(OpOrArgument::Op(op))?;
self.parse_semicolon()?;
Ok(op)
}
pub fn parse_custom_operation_name(&mut self) -> ParseResult<Span<OperationName>> {
let (name_span, name) = self
.token_stream_mut()
.expect_map("operation name", |tok| match tok {
Token::BareIdent(id) => Some(id),
_ => None,
})?
.into_parts();
let (dialect, opcode) = name.split_once('.').unwrap_or_else(|| {
(self.state_mut().default_dialect_stack.last().unwrap().as_str(), name)
});
let dialect = self.context().get_registered_dialect(interner::Symbol::intern(dialect));
dialect
.registered_ops()
.iter()
.find(|name| name.name() == opcode)
.cloned()
.map(|name| Span::new(name_span, name))
.ok_or(ParserError::UnknownOperation { span: name_span })
}
}
impl<'input, P> OperationParser<P>
where
P: Parser<'input>,
{
pub fn parse_region(
&mut self,
region: RegionRef,
entry_arguments: &[Argument],
isolated: bool,
) -> ParseResult {
self.parser.parse_lbrace()?;
if let Some(asm_state) = self.state_mut().asm_state.as_deref_mut() {
asm_state.start_region_definition();
}
if !entry_arguments.is_empty()
|| self.token_stream_mut().is_next(|tok| !matches!(tok, Token::Rbrace))
{
let start = self.parser.current_location();
self.parse_region_body(region, start, entry_arguments, isolated)?;
}
self.parser.parse_rbrace()?;
if let Some(asm_state) = self.state_mut().asm_state.as_deref_mut() {
asm_state.finalize_region_definition();
}
Ok(())
}
pub fn parse_region_body(
&mut self,
mut region: RegionRef,
start: SourceSpan,
entry_arguments: &[Argument],
isolated: bool,
) -> ParseResult {
let ip = *self.builder().insertion_point();
self.push_ssa_name_scope(isolated);
let owning_block = self.builder().context_rc().create_block();
if !self
.parser
.token_stream_mut()
.is_next(|tok| matches!(tok, Token::CaretIdent(_)))
&& let Some(asm_state) = self.parser.state_mut().asm_state.as_deref_mut()
{
asm_state.add_block_definition(owning_block, start);
}
if !entry_arguments.is_empty() && entry_arguments[0].name.name.is_user_defined() {
if self.token_stream_mut().is_next(|tok| matches!(tok, Token::CaretIdent(_))) {
return Err(ParserError::BlockNameInRegionWithNamedArgs {
span: self.parser.current_location(),
});
}
for arg in entry_arguments {
let arg_info = arg.name;
if let Some(def_loc) = self.get_reference_loc(arg_info.name) {
return Err(ParserError::RegionArgumentAlreadyDefined {
arg: arg_info.name,
span: arg_info.loc,
prev_span: def_loc,
});
}
let arg = self
.context()
.append_block_argument(owning_block, arg.ty.clone(), arg_info.loc)
.borrow()
.downcast_ref::<BlockArgument>()
.unwrap()
.as_block_argument_ref();
if let Some(asm_state) = self.state_mut().asm_state.as_deref_mut() {
asm_state.add_block_argument_definition(arg, arg_info.loc);
}
self.add_definition(arg_info, arg as ValueRef)?;
}
}
self.parse_block(Some(owning_block))?;
if !entry_arguments.is_empty()
&& owning_block.borrow().num_arguments() > entry_arguments.len()
{
return Err(ParserError::EntryBlockArgumentsAlreadyDefined { span: start });
}
region.borrow_mut().body_mut().push_back(owning_block);
while self.token_stream_mut().is_next(|tok| !matches!(tok, Token::Rbrace)) {
let new_block = self.context_rc().create_block();
self.parse_block(Some(new_block))?;
region.borrow_mut().push_back(new_block);
}
self.pop_ssa_name_scope()?;
self.builder_mut().restore_insertion_point(ip);
Ok(())
}
}
impl<'input, P> OperationParser<P>
where
P: Parser<'input>,
{
pub fn parse_block(&mut self, block: Option<BlockRef>) -> ParseResult {
if let Some(block) = block
&& self.token_stream_mut().is_next(|tok| !matches!(tok, Token::CaretIdent(_)))
{
return self.parse_block_body(block);
}
let (name_span, name) = self
.token_stream_mut()
.expect_map("block name", |tok| match tok {
Token::CaretIdent(name) => {
Some(BlockId::from_symbol(interner::Symbol::intern(name)))
}
_ => None,
})?
.into_parts();
let block_and_loc = self.get_block_info_by_name(name);
let block = if let Some(block) = block_and_loc {
if !self.erase_forward_ref(block.into_inner()) {
return Err(ParserError::BlockAlreadyDefined {
span: name_span,
name,
});
}
Span::new(name_span, block.into_inner())
} else {
Span::new(name_span, block.unwrap_or_else(|| self.context_rc().create_block()))
};
if let Some(asm_state) = self.state_mut().asm_state.as_deref_mut() {
asm_state.add_block_definition(block.into_inner(), name_span);
}
if self.token_stream_mut().is_next(|tok| matches!(tok, Token::Lparen)) {
self.parse_optional_block_arg_list(block.into_inner())?;
}
self.parse_colon()?;
self.parse_block_body(block.into_inner())
}
pub fn parse_block_body(&mut self, block: BlockRef) -> ParseResult {
self.builder_mut().set_insertion_point_to_end(block);
while self
.token_stream_mut()
.is_next(|tok| !matches!(tok, Token::CaretIdent(_) | Token::Rbrace))
{
self.parse_operation()?;
}
Ok(())
}
pub fn parse_optional_block_arg_list(&mut self, owner: BlockRef) -> ParseResult {
if self.token_stream_mut().is_next(|tok| matches!(tok, Token::Rbrace)) {
return Ok(());
}
let defining_existing_args = owner.borrow().has_arguments();
let mut next_argument = 0usize;
let context = self.context_rc();
self.parse_comma_separated_list(Delimiter::Paren, Some("block argument list"), |parser| {
parser.parse_ssa_def_or_use_and_type(|parser, use_info, ty| {
let arg = if defining_existing_args {
let owner = owner.borrow();
if next_argument >= owner.num_arguments() {
return Err(ParserError::TooManyBlockArguments { span: use_info.loc });
}
let arg = owner.arguments()[next_argument];
next_argument += 1;
let arg_borrowed = arg.borrow();
if arg_borrowed.ty() != &ty {
return Err(ParserError::BlockArgumentTypeMismatch {
span: use_info.loc,
arg: arg_borrowed.id(),
ty: arg_borrowed.ty().clone(),
expected: ty.clone(),
});
}
arg
} else {
let arg = context.append_block_argument(owner, ty, use_info.loc);
arg.downcast_value::<BlockArgument>()
};
parser.parse_trailing_location_specifier(OpOrArgument::Arg(arg))?;
if let Some(asm_state) = parser.state_mut().asm_state.as_deref_mut() {
asm_state.add_block_argument_definition(arg, use_info.loc);
}
parser.add_definition(use_info, arg)
})?;
Ok(true)
})
}
pub fn get_block_named(&mut self, name: BlockId, loc: SourceSpan) -> Span<BlockRef> {
let block = match self.get_block_info_by_name(name) {
Some(block) => block,
None => {
let block = self.context_rc().create_block();
self.blocks_by_name.last_mut().unwrap().insert(name, Span::new(loc, block));
self.insert_forward_ref(block, loc);
Span::new(loc, block)
}
};
if let Some(asm_state) = self.state_mut().asm_state.as_deref_mut() {
asm_state.add_block_uses(block.into_inner(), &[loc]);
}
block
}
}
impl<'input, P> OperationParser<P>
where
P: Parser<'input>,
{
fn get_block_info_by_name(&self, name: BlockId) -> Option<Span<BlockRef>> {
self.blocks_by_name.last()?.get(&name).copied()
}
fn insert_forward_ref(&mut self, block: BlockRef, loc: SourceSpan) {
self.forward_ref.last_mut().unwrap().insert_new(block, loc);
}
fn erase_forward_ref(&mut self, block: BlockRef) -> bool {
self.forward_ref.last_mut().unwrap().remove(&block).is_some()
}
fn is_forward_ref_placeholder(&self, value: ValueRef) -> bool {
self.forward_ref_placeholders.contains_key(&value)
}
}
impl<P> Drop for OperationParser<P> {
fn drop(&mut self) {
for mut op in self.forward_ref_ops.drain(..) {
let mut op = op.borrow_mut();
op.drop_all_uses();
op.erase();
}
for scope in self.forward_ref.drain(..) {
for (mut fwd, _) in scope.into_iter() {
fwd.borrow_mut().drop_all_uses();
}
}
}
}
struct CleanupOpStateRegions<'a> {
state: &'a mut OperationState,
}
impl<'a> Deref for CleanupOpStateRegions<'a> {
type Target = OperationState;
#[inline]
fn deref(&self) -> &Self::Target {
&*self.state
}
}
impl<'a> DerefMut for CleanupOpStateRegions<'a> {
#[inline]
fn deref_mut(&mut self) -> &mut Self::Target {
self.state
}
}
impl<'a> Drop for CleanupOpStateRegions<'a> {
fn drop(&mut self) {
for mut region in self.state.regions.iter_mut() {
let mut region = region.borrow_mut();
let mut body = region.body_mut().front_mut();
loop {
if let Some(mut block) = body.get_mut() {
block.drop_all_defined_value_uses();
} else {
break;
}
body.move_next();
}
}
}
}
struct CustomOpAsmParser<'a, P> {
span: SourceSpan,
op_name: OperationName,
parser: &'a mut OperationParser<P>,
result_ids: &'a [ResultRecord],
parse_assembly: ParseAssemblyFn,
isolated_from_above: bool,
}
impl<'a, 'input: 'a, P> CustomOpAsmParser<'a, P>
where
P: Parser<'input>,
{
pub fn new(
span: SourceSpan,
result_ids: &'a [ResultRecord],
parse_assembly: ParseAssemblyFn,
op_name: OperationName,
isolated_from_above: bool,
parser: &'a mut OperationParser<P>,
) -> Self {
Self {
span,
op_name,
parser,
result_ids,
parse_assembly,
isolated_from_above,
}
}
pub fn parse_operation(&mut self, state: &mut OperationState) -> ParseResult {
let parse_assembly = self.parse_assembly;
parse_assembly(state, self)?;
state.attrs.sort_by_key(|attr| attr.name);
for (i, attr) in state.attrs.iter().enumerate() {
if state.attrs.get(i + 1).is_some_and(|attr2| attr.name == attr2.name) {
return Err(ParserError::DuplicateAttribute {
span: self.span,
name: attr.name,
});
}
}
Ok(())
}
}
impl<'a, 'input: 'a, P> Parser<'input> for CustomOpAsmParser<'a, P>
where
P: Parser<'input>,
{
fn builder(&self) -> &OpBuilder {
self.parser.builder()
}
fn builder_mut(&mut self) -> &mut OpBuilder {
self.parser.builder_mut()
}
fn state(&self) -> &ParserState<'input> {
self.parser.state()
}
fn state_mut(&mut self) -> &mut ParserState<'input> {
self.parser.state_mut()
}
fn token_stream(&self) -> &TokenStream<'input> {
self.parser.token_stream()
}
fn token_stream_mut(&mut self) -> &mut TokenStream<'input> {
self.parser.token_stream_mut()
}
fn context<'p>(&'p self) -> &'p Context
where
'input: 'p,
{
self.parser.context()
}
fn context_rc(&self) -> Rc<Context> {
self.parser.context_rc()
}
fn source_manager<'p>(&'p self) -> &'p dyn SourceManager
where
'input: 'p,
{
self.parser.source_manager()
}
fn source_id(&self) -> SourceId {
self.parser.source_id()
}
fn current_location(&self) -> SourceSpan {
self.parser.current_location()
}
fn parse_attribute(&mut self, ty: &Type) -> ParseResult<Span<AttributeRef>> {
self.parser.parse_attribute(ty)
}
fn parse_extended_attribute(&mut self, ty: &Type) -> ParseResult<Span<AttributeRef>> {
self.parser.parse_extended_attribute(ty)
}
fn parse_optional_attribute(&mut self, ty: &Type) -> ParseResult<Option<Span<AttributeRef>>> {
self.parser.parse_optional_attribute(ty)
}
fn parse_type(&mut self) -> ParseResult<Span<Type>> {
self.parser.parse_type()
}
fn parse_optional_type(&mut self) -> ParseResult<Option<Span<Type>>> {
self.parser.parse_optional_type()
}
fn parse_type_list(&mut self, result: &mut SmallVec<[Type; 4]>) -> ParseResult {
self.parser.parse_type_list(result)
}
fn parse_type_list_no_parens(&mut self, result: &mut SmallVec<[Type; 4]>) -> ParseResult {
self.parser.parse_type_list_no_parens(result)
}
fn parse_function_result_types(&mut self) -> ParseResult<SmallVec<[Type; 1]>> {
self.parser.parse_function_result_types()
}
fn parse_dialect_symbol_body(&mut self) -> ParseResult<Span<CompactString>> {
self.parser.parse_dialect_symbol_body()
}
fn parse_extended_type(&mut self) -> ParseResult<Span<Type>> {
self.parser.parse_extended_type()
}
fn parse_function_type(&mut self) -> ParseResult<Span<FunctionType>> {
self.parser.parse_function_type()
}
fn parse_non_function_type(&mut self) -> ParseResult<Span<Type>> {
self.parser.parse_non_function_type()
}
fn parse_tuple_type(&mut self) -> ParseResult<Span<Type>> {
self.parser.parse_tuple_type()
}
fn parse_attribute_dict(&mut self, attrs: &mut ParsedAttrs) -> ParseResult {
self.parser.parse_attribute_dict(attrs)
}
fn parse_optional_attribute_dict(&mut self, attrs: &mut ParsedAttrs) -> ParseResult {
self.parser.parse_optional_attribute_dict(attrs)
}
fn parse_optional_attribute_dict_with_keyword(
&mut self,
attrs: &mut ParsedAttrs,
) -> ParseResult {
self.parser.parse_optional_attribute_dict_with_keyword(attrs)
}
fn parse_dec_or_hex_attr(
&mut self,
ty: &Type,
is_negative: bool,
) -> ParseResult<Span<AttributeRef>> {
self.parser.parse_dec_or_hex_attr(ty, is_negative)
}
}
impl<'a, 'input: 'a, P> OpAsmParser<'input> for CustomOpAsmParser<'a, P>
where
P: Parser<'input>,
{
#[inline]
fn parse_generic_operation(&mut self, ip: Option<ProgramPoint>) -> ParseResult<OperationRef> {
self.parser.parse_generic_operation(ip)
}
#[inline]
fn parse_generic_operation_after_name(
&mut self,
result: &mut OperationState,
parsed_operand_use_info: Option<&[UnresolvedOperand]>,
parsed_successors: Option<&[BlockRef]>,
parsed_regions: Option<&[RegionRef]>,
parsed_attributes: Option<ParsedAttrs>,
parsed_fn_type: Option<FunctionType>,
) -> ParseResult {
self.parser.parse_generic_operation_after_name(
result,
parsed_operand_use_info,
parsed_successors,
parsed_regions,
parsed_attributes,
parsed_fn_type,
)
}
#[inline]
fn parse_custom_operation_name(&mut self) -> ParseResult<Span<OperationName>> {
self.parser.parse_custom_operation_name()
}
fn get_result_name(&self, mut result_num: u8) -> Option<(interner::Symbol, u8)> {
for entry in self.result_ids {
if result_num < entry.count {
return Some((entry.id, result_num));
}
result_num -= entry.count;
}
None
}
fn get_num_results(&self) -> usize {
self.result_ids.iter().map(|entry| entry.count as usize).sum()
}
fn parse_operand(&mut self, allow_result_number: bool) -> ParseResult<UnresolvedOperand> {
self.parser.parse_ssa_use(allow_result_number)
}
fn parse_optional_operand(
&mut self,
allow_result_number: bool,
) -> ParseResult<Option<UnresolvedOperand>> {
if self
.parser
.token_stream_mut()
.is_next(|tok| matches!(tok, Token::PercentIdent(_)))
{
self.parse_operand(allow_result_number).map(Some)
} else {
Ok(None)
}
}
fn resolve_operand(&mut self, operand: UnresolvedOperand, ty: Type) -> ParseResult<ValueRef> {
self.parser.resolve_ssa_use(operand, ty)
}
fn parse_argument(&mut self, allow_type: bool, allow_attrs: bool) -> ParseResult<Argument> {
let name = self.parse_operand( false)?;
let ty = if allow_type {
Some(self.parse_colon_type()?)
} else {
None
};
let mut attrs = SmallVec::new_const();
if allow_attrs {
self.parse_optional_attribute_dict(&mut attrs)?;
}
let loc = self.parse_optional_location_specifier()?.unwrap_or(Location::Unknown);
Ok(Argument {
name,
ty: ty.map(|t| t.into_inner()).unwrap_or(Type::Unknown),
attrs,
loc,
})
}
fn parse_optional_argument(
&mut self,
allow_type: bool,
allow_attrs: bool,
) -> ParseResult<Option<Argument>> {
if self
.parser
.token_stream_mut()
.is_next(|tok| matches!(tok, Token::PercentIdent(_)))
{
self.parse_argument(allow_type, allow_attrs).map(Some)
} else {
Ok(None)
}
}
fn parse_region(
&mut self,
region: RegionRef,
arguments: &[Argument],
enable_name_shadowing: bool,
) -> ParseResult {
assert!(
!enable_name_shadowing || self.isolated_from_above,
"name shadowing is only allowed on isolated regions"
);
self.parser.parse_region(region, arguments, enable_name_shadowing)
}
fn parse_optional_region(
&mut self,
arguments: &[Argument],
enable_name_shadowing: bool,
) -> ParseResult<Option<RegionRef>> {
if self.parser.token_stream_mut().is_next(|tok| matches!(tok, Token::Lbrace)) {
let new_region = self.context().create_region();
self.parse_region(new_region, arguments, enable_name_shadowing)?;
Ok(Some(new_region))
} else {
Ok(None)
}
}
fn parse_successor(&mut self) -> ParseResult<Span<BlockRef>> {
self.parser.parse_successor()
}
fn parse_optional_successor(&mut self) -> ParseResult<Option<Span<BlockRef>>> {
if self
.parser
.token_stream_mut()
.is_next(|tok| matches!(tok, Token::CaretIdent(_)))
{
self.parse_successor().map(Some)
} else {
Ok(None)
}
}
fn parse_successor_and_use_list(
&mut self,
operands: &mut SmallVec<[ValueRef; 2]>,
) -> ParseResult<Span<BlockRef>> {
let dest = self.parse_successor()?;
self.parse_optional_lparen()?;
self.parser.parse_optional_ssa_use_and_type_list(operands)?;
self.parse_rparen()?;
Ok(dest)
}
fn parse_optional_location_specifier(&mut self) -> ParseResult<Option<Location>> {
if !self.parser.token_stream_mut().next_if_eq(Token::Loc)? {
return Ok(None);
}
self.parser.parse_lparen()?;
let loc = if self
.parser
.token_stream_mut()
.is_next(|tok| matches!(tok, Token::HashIdent(id) if !id.contains('.')))
{
self.parser.parse_location_alias()?
} else {
self.parser.parse_location_instance()?
};
self.parser.parse_rparen()?;
Ok(Some(loc))
}
}
pub struct TopLevelOperationParser<P> {
parser: P,
}
impl<'input, P> TopLevelOperationParser<P>
where
P: Parser<'input>,
{
pub const fn new(parser: P) -> Self {
Self { parser }
}
pub fn parse(self, loc: SourceSpan) -> ParseResult<WorldRef> {
use crate::{BuilderExt, dialects::builtin::World};
let Self { mut parser } = self;
let mut top_level_op = parser.builder_mut().create::<World, ()>(loc)()?;
let mut op_parser = OperationParser::new(parser, top_level_op);
loop {
let Some((start, next_token, end)) = op_parser.token_stream_mut().peek()? else {
op_parser.finalize()?;
break Ok(top_level_op);
};
match next_token {
Token::HashIdent(_) => {
parse_attribute_alias_def(&mut op_parser)?;
}
Token::BangIdent(_) => {
parse_type_alias_def(&mut op_parser)?;
}
Token::FileMetadataStart => {
parse_file_metadata_dictionary(&mut op_parser)?;
}
_ => {
op_parser.parse_operation()?;
}
}
}
}
}
fn parse_attribute_alias_def<'input, P>(parser: &mut OperationParser<P>) -> ParseResult
where
P: Parser<'input>,
{
let (span, id) = parser
.token_stream_mut()
.expect_map("#-identifier", |tok| match tok {
Token::HashIdent(id) => Some(interner::Symbol::intern(id)),
_ => None,
})?
.into_parts();
if parser.state().symbols.attribute_alias_definitions.contains_key(&id) {
return Err(ParserError::AttributeAliasAlreadyDefined { span, id });
}
if id.as_str().contains('.') {
return Err(ParserError::InvalidAttributeAliasName {
span,
id,
reason: "attribute names with a '.' are reserved for dialect-defined names".to_string(),
});
}
parser.parse_equal()?;
let (alias_span, attr) = parser.parse_attribute(&Type::Unknown)?.into_parts();
let state = parser.state_mut();
if let Some(asm_state) = state.asm_state.as_deref_mut() {
asm_state.add_attr_alias_definition(id, span, Some(attr));
}
state.symbols.attribute_alias_definitions.insert(id, Span::new(span, attr));
Ok(())
}
fn parse_type_alias_def<'input, P>(parser: &mut OperationParser<P>) -> ParseResult
where
P: Parser<'input>,
{
let (span, id) = parser
.token_stream_mut()
.expect_map("!-identifier", |tok| match tok {
Token::BangIdent(id) => Some(interner::Symbol::intern(id)),
_ => None,
})?
.into_parts();
if parser.state().symbols.type_alias_definitions.contains_key(&id) {
return Err(ParserError::TypeAliasAlreadyDefined { span, id });
}
if id.as_str().contains('.') {
return Err(ParserError::InvalidTypeAliasName {
span,
id,
reason: "type names with a '.' are reserved for dialect-defined names".to_string(),
});
}
parser.parse_equal()?;
let (alias_span, ty) = parser.parse_type()?.into_parts();
let span = SourceSpan::new(span.source_id(), span.start()..alias_span.end());
let state = parser.state_mut();
if let Some(asm_state) = state.asm_state.as_deref_mut() {
asm_state.add_type_alias_definition(id, span, ty.clone());
}
state.symbols.type_alias_definitions.insert(id, Span::new(span, ty));
Ok(())
}
fn parse_file_metadata_dictionary<'input, P>(parser: &mut OperationParser<P>) -> ParseResult
where
P: Parser<'input>,
{
parser.token_stream_mut().expect(Token::FileMetadataStart)?;
parser.parse_comma_separated_list_until(
Token::FileMetadataEnd,
false,
|parser| {
let Some(key) = parser
.parse_optional_keyword()?
.map(|token| token.map(|t| t.into_compact_string()))
else {
return Ok(false);
};
parser.parse_colon()?;
let (span, key) = key.into_parts();
Err(ParserError::UnknownFileMetadata { span, key })
},
)
}