Skip to content

Commit

Permalink
First working example of const generics
Browse files Browse the repository at this point in the history
  • Loading branch information
adam-mcdaniel committed Sep 5, 2024
1 parent f183292 commit c899528
Show file tree
Hide file tree
Showing 12 changed files with 545 additions and 259 deletions.
19 changes: 13 additions & 6 deletions src/frontend/parse.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2617,7 +2617,7 @@ fn parse_struct_stmt<'a, E: ParseError<&'a str> + ContextError<&'a str>>(
Declaration::Type(
name.to_owned(),
Type::Poly(
params.into_iter().map(|x| x.to_owned()).collect(),
params,
Type::Struct(fields).into(),
),
),
Expand Down Expand Up @@ -2678,7 +2678,7 @@ fn parse_enum_stmt<'a, E: ParseError<&'a str> + ContextError<&'a str>>(
Declaration::Type(
name.to_owned(),
Type::Poly(
params.into_iter().map(|x| x.to_owned()).collect(),
params,
Type::EnumUnion(fields).into(),
),
),
Expand Down Expand Up @@ -2949,20 +2949,26 @@ fn parse_type_enum<'a, E: ParseError<&'a str> + ContextError<&'a str>>(

fn parse_type_params<'a, E: ParseError<&'a str> + ContextError<&'a str>>(
input: &'a str,
) -> IResult<&'a str, Vec<String>, E> {
) -> IResult<&'a str, Vec<(String, Option<Type>)>, E> {
let (input, _) = tag("<")(input)?;
let (input, _) = whitespace(input)?;
let (input, mut params) = many0(terminated(parse_symbol, tag(",")))(input)?;
let (input, mut params) = many0(terminated(alt((
map(pair(parse_symbol, delimited(terminated(whitespace, tag(":")), parse_type, whitespace)), |(name, ty)| (name, Some(ty))),
map(parse_symbol, |x| (x, None))
)), tag(",")))(input)?;
let (input, _) = whitespace(input)?;
let (input, last_param) = opt(parse_symbol)(input)?;
let (input, last_param) = opt(alt((
map(pair(parse_symbol, delimited(terminated(whitespace, tag(":")), parse_type, whitespace)), |(name, ty)| (name, Some(ty))),
map(parse_symbol, |x| (x, None))
)))(input)?;
let (input, _) = whitespace(input)?;
let (input, _) = tag(">")(input)?;

if let Some(last_param) = last_param {
params.push(last_param);
}

Ok((input, params.into_iter().map(|x| x.to_string()).collect()))
Ok((input, params.into_iter().map(|(name, ty)| (name.to_string(), ty)).collect()))
}

fn parse_type_function<'a, E: ParseError<&'a str> + ContextError<&'a str>>(
Expand Down Expand Up @@ -3053,6 +3059,7 @@ fn parse_type_atom<'a, E: ParseError<&'a str> + ContextError<&'a str>>(
parse_type_function,
map(parse_symbol, |x| Type::Symbol(x.to_string())),
parse_type_group,
map(parse_const_atom, |x| Type::ConstParam(x.into()))
))(input)?;

Ok((input, ty))
Expand Down
12 changes: 8 additions & 4 deletions src/lir/compile.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1071,13 +1071,17 @@ impl Compile for ConstExpr {
let ty = self.get_type(env)?;
// Compile the constant expression.
match self {
Self::Template(_, _) => {
Self::Any
| Self::Template(_, _) => {
// Cannot compile a template expression.
return Err(Error::UnsizedType(ty));
}

Self::Type(_) => {
// Do nothing.
Self::Type(t) => {
match t.simplify(env)? {
Type::ConstParam(cexpr) => cexpr.compile_expr(env, output)?,
_ => {}
}
}
Self::Member(container, member) => {
let new_container = *container.clone();
Expand Down Expand Up @@ -1164,7 +1168,7 @@ impl Compile for ConstExpr {
}

let mut result = *result.clone();
for (param, ty_arg) in params.into_iter().zip(ty_args) {
for ((param, _), ty_arg) in params.into_iter().zip(ty_args) {
result.substitute(&param, &ty_arg);
}
result = result.eval(env)?;
Expand Down
46 changes: 27 additions & 19 deletions src/lir/env.rs
Original file line number Diff line number Diff line change
Expand Up @@ -256,7 +256,7 @@ impl Env {
let template = other_ty.clone();

let ty_params = template.get_template_params(self);
let ty_param_set = ty_params.clone().into_iter().collect::<HashSet<_>>();
let ty_param_set = ty_params.clone().into_iter().map(|x| x.0).collect::<HashSet<_>>();
let monomorph = ty.clone();
debug!("Monomorph of {template} is {monomorph}");
let mut symbols = HashMap::new();
Expand All @@ -272,12 +272,12 @@ impl Env {
debug!("Failed to get monomorph template args for {monomorph} of {template}");
continue;
}
for (symbol, ty) in &symbols {
debug!("----> {symbol} == {ty}");
for (symbol, (ty, specifier)) in &symbols {
debug!("----> {symbol} == {ty}: {specifier:?}");
}
let template_associated_consts = consts.clone();
let mut ty_args = Vec::new();
for ty_param in &ty_params {
for (ty_param, _) in &ty_params {
if let Some(arg) = symbols.get(ty_param) {
ty_args.push(arg.clone());
} else {
Expand All @@ -300,7 +300,7 @@ impl Env {
debug!("Found cached associated const (type) {name} of type {const_ty}");
// let result = const_ty.apply(ty_args.clone()).simplify_until_simple(self).ok()?;
// let result = const_ty.apply(ty_args.clone());
let result = const_ty.apply(ty_args.clone());
let result = const_ty.apply(ty_args.clone().into_iter().map(|x|x.0).collect());
match result.simplify_until_simple(self) {
Ok(result) => {
debug!("Found associated const (type) {name} of type {ty} = {result}");
Expand All @@ -325,7 +325,7 @@ impl Env {
}

if !ty.can_decay_to(other_ty, self).unwrap_or(false) {
trace!("Type {other_ty} does not equal {ty}");
// trace!("Type {other_ty} does not equal {ty}");
continue;
}
if let Some((constant, expr_ty)) = consts.get(name) {
Expand Down Expand Up @@ -381,7 +381,7 @@ impl Env {
let monomorph = ty.clone();
let mut symbols = HashMap::new();
let ty_params = template.get_template_params(self);
let ty_param_set = ty_params.clone().into_iter().collect::<HashSet<_>>();
let ty_param_set = ty_params.clone().into_iter().map(|x| x.0).collect::<HashSet<_>>();
if monomorph
.get_monomorph_template_args(
&template.strip_template(self),
Expand All @@ -394,13 +394,13 @@ impl Env {
debug!("Failed to get monomorph template args for {monomorph} of {template}");
continue;
}
for (symbol, ty) in &symbols {
debug!("----> {symbol} == {ty}");
}
// for (symbol, ty) in &symbols {
// debug!("----> {symbol} == {ty}");
// }
let template_associated_consts = consts.clone();
let mut ty_args = Vec::new();
for ty_param in &ty_params {
if let Some(arg) = symbols.get(ty_param) {
for (ty_param, _) in &ty_params {
if let Some((arg, _)) = symbols.get(ty_param) {
ty_args.push(arg.clone());
} else {
continue;
Expand Down Expand Up @@ -541,7 +541,7 @@ impl Env {
}

pub(super) fn has_any_associated_const(&self, ty: &Type) -> bool {
trace!("Checking if type {ty} has any associated constants");
// trace!("Checking if type {ty} has any associated constants");
let associated_constants = self.associated_constants.read().unwrap();
if let Some(consts) = associated_constants.get(ty) {
if !consts.is_empty() {
Expand All @@ -554,7 +554,7 @@ impl Env {
continue;
}
if !ty.can_decay_to(other_ty, self).unwrap_or(false) {
trace!("Type {other_ty} does not equal {ty}");
// trace!("Type {other_ty} does not equal {ty}");
continue;
}
trace!("Found eligible type {other_ty} for {ty}");
Expand Down Expand Up @@ -640,7 +640,7 @@ impl Env {
// Strip off the template parameters from the type arguments.
let mono_const = if let ConstExpr::Template(ty_params, cexpr) = const_expr {
let mut tmp = *cexpr.clone();
for (param, arg) in ty_params.iter().zip(ty_args.iter()) {
for ((param, _), arg) in ty_params.iter().zip(ty_args.iter()) {
tmp.substitute(param, arg);
}
tmp
Expand Down Expand Up @@ -773,7 +773,7 @@ impl Env {
// if !self.types.contains_key(&name) {
self.define_const(&name, access.clone());
if let Ok(Type::Type(ty)) = access.get_type(self) {
self.define_type(name, *ty);
self.define_type(&name, *ty);
}
}
}
Expand Down Expand Up @@ -852,7 +852,7 @@ impl Env {

for (name, associated_const) in impls {
let templated_const =
associated_const.template(supplied_param_symbols.clone());
associated_const.template(template_params.clone());
self.add_associated_const(*template.clone(), name, templated_const)?;
}
} else {
Expand Down Expand Up @@ -999,14 +999,22 @@ impl Env {
Type::Symbol(sym) if sym == &name => {
trace!("Defining type {ty} to itself as {name}");
}
Type::ConstParam(cexpr) => {
trace!("Defining constant param: {name} => {cexpr}");
self.define_const(&name, *cexpr.clone());
}
_ => {
trace!("Defining type {name} as {ty}");
Arc::make_mut(&mut self.consts).insert(name.clone(), ConstExpr::Type(ty.clone()));
Arc::make_mut(&mut self.types).insert(name, ty.clone());
Arc::make_mut(&mut self.types).insert(name.clone(), ty.clone());

if let Ok(simplified) = ty.simplify_until_concrete(self) {
if let Ok(size) = simplified.get_size(self) {
self.set_precalculated_size(simplified, size);
self.set_precalculated_size(simplified.clone(), size);
}
if let Type::ConstParam(cexpr) = simplified {
trace!("Found const param \"{name}\": {cexpr}");
self.define_const(&name, *cexpr);
}
}
}
Expand Down
9 changes: 8 additions & 1 deletion src/lir/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,11 @@ pub enum Error {
),

UnimplementedOperator(String),

/// An error caused by unexpectedly passing a constant parameter to a template.
UnexpectedConstParam {
found: Type,
expr: Expr
},
/// An error caused by trying to assemble invalid code generated by the compiler.
/// This should be taken seriously, unless the error is due to an invalid handwritten builtin.
AssemblyError(crate::asm::Error),
Expand Down Expand Up @@ -158,6 +162,9 @@ impl From<crate::asm::Error> for Error {
impl Display for Error {
fn fmt(&self, f: &mut Formatter) -> FmtResult {
match self {
Self::UnexpectedConstParam { found, expr } => {
write!(f, "unexpected constant parameter {found} in expression {expr}")
}
Self::Annotated(err, _) => {
write!(f, "{err}")
}
Expand Down
Loading

0 comments on commit c899528

Please sign in to comment.