Anforderungen  |   Konzepte  |   Entwurf  |   Entwicklung  |   Qualitätssicherung  |   Lebenszyklus  |   Steuerung
 
 
 
 


Quelle  lowering.rs   Sprache: unbekannt

 
Spracherkennung für: .rs vermutete Sprache: Unknown {[0] [0] [0]} [Methode: Schwerpunktbildung, einfache Gewichte, sechs Dimensionen]

use super::{
    AttributeContext, AttributeValidator, Attrs, Borrow, BoundedLifetime, EnumDef, EnumPath,
    EnumVariant, IdentBuf, IntType, Lifetime, LifetimeEnv, LifetimeLowerer, LookupId, MaybeOwn,
    Method, NonOptional, OpaqueDef, OpaquePath, Optional, OutStructDef, OutStructField,
    OutStructPath, OutType, Param, ParamLifetimeLowerer, ParamSelf, PrimitiveType,
    ReturnLifetimeLowerer, ReturnType, ReturnableStructPath, SelfParamLifetimeLowerer, SelfType,
    Slice, SpecialMethod, SpecialMethodPresence, StructDef, StructField, StructPath, SuccessType,
    Type, TypeDef, TypeId,
};
use crate::ast::attrs::AttrInheritContext;
use crate::{ast, Env};
use core::fmt;
use strck_ident::IntoCk;

/// An error from lowering the AST to the HIR.
#[derive(Debug)]
#[non_exhaustive]
pub enum LoweringError {
    /// The purpose of having this is that translating to the HIR has enormous
    /// potential for really detailed error handling and giving suggestions.
    ///
    /// Unfortunately, working out what the error enum should look like to enable
    /// this is really hard. The plan is that once the lowering code is completely
    /// written, we ctrl+F for `"LoweringError::Other"` in the lowering code, and turn every
    /// instance into an specialized enum variant, generalizing where possible
    /// without losing any information.
    Other(String),
}

impl fmt::Display for LoweringError {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match *self {
            Self::Other(ref s) => s.fmt(f),
        }
    }
}

#[derive(Default, Clone)]
pub struct ErrorContext {
    item: String,
    subitem: Option<String>,
}

impl fmt::Display for ErrorContext {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        if let Some(ref subitem) = self.subitem {
            write!(f, "{}::{subitem}", self.item)
        } else {
            self.item.fmt(f)
        }
    }
}

impl fmt::Debug for ErrorContext {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        fmt::Display::fmt(self, f)
    }
}

/// An error store, which one can push errors to. It keeps track of the
/// current "context" for the error, usually a type or a type::method. `'tree`
/// is the AST/HIR tree it is temporarily borrowing from for the context.
#[derive(Default)]
pub struct ErrorStore<'tree> {
    /// The errors
    errors: Vec<ErrorAndContext>,
    /// The current context (types, modules)
    item: &'tree str,
    /// The current sub-item context (methods, etc)
    subitem: Option<&'tree str>,
}

pub type ErrorAndContext = (ErrorContext, LoweringError);

impl<'tree> ErrorStore<'tree> {
    /// Push an error to the error store
    pub fn push(&mut self, error: LoweringError) {
        let context = ErrorContext {
            item: self.item.into(),
            subitem: self.subitem.map(|s| s.into()),
        };
        self.errors.push((context, error));
    }

    pub(super) fn take_errors(&mut self) -> Vec<ErrorAndContext> {
        core::mem::take(&mut self.errors)
    }

    pub(super) fn is_empty(&self) -> bool {
        self.errors.is_empty()
    }

    pub(super) fn set_item(&mut self, item: &'tree str) {
        self.item = item;
        self.subitem = None;
    }
    pub(super) fn set_subitem(&mut self, subitem: &'tree str) {
        self.subitem = Some(subitem);
    }
}

pub(super) struct LoweringContext<'ast> {
    pub lookup_id: LookupId<'ast>,
    pub errors: ErrorStore<'ast>,
    pub env: &'ast Env,
    pub attr_validator: Box<dyn AttributeValidator>,
}

/// An item and the info needed to
pub(crate) struct ItemAndInfo<'ast, Ast> {
    pub(crate) item: &'ast Ast,
    pub(crate) in_path: &'ast ast::Path,
    /// Any parent attributes resolved from the module, for a type context
    pub(crate) ty_parent_attrs: Attrs,

    /// Any parent attributes resolved from the module, for a method context
    pub(crate) method_parent_attrs: Attrs,
    pub(crate) id: TypeId,
}

impl<'ast> LoweringContext<'ast> {
    /// Lowers an [`ast::Ident`]s into an [`hir::IdentBuf`].
    ///
    /// If there are any errors, they're pushed to `errors` and `Err` is returned.
    pub(super) fn lower_ident(
        &mut self,
        ident: &ast::Ident,
        context: &'static str,
    ) -> Result<IdentBuf, ()> {
        match ident.as_str().ck() {
            Ok(name) => Ok(name.to_owned()),
            Err(e) => {
                self.errors.push(LoweringError::Other(format!(
                    "Ident `{ident}` from {context} could not be turned into a Rust ident: {e}"
                )));
                Err(())
            }
        }
    }

    /// Lowers multiple items at once
    fn lower_all<Ast: 'static, Hir>(
        &mut self,
        ast_defs: impl ExactSizeIterator<Item = ItemAndInfo<'ast, Ast>>,
        lower: impl Fn(&mut Self, ItemAndInfo<'ast, Ast>) -> Result<Hir, ()>,
    ) -> Result<Vec<Hir>, ()> {
        let mut hir_types = Ok(Vec::with_capacity(ast_defs.len()));

        for def in ast_defs {
            let hir_type = lower(self, def);

            match (hir_type, &mut hir_types) {
                (Ok(hir_type), Ok(hir_types)) => hir_types.push(hir_type),
                _ => hir_types = Err(()),
            }
        }

        hir_types
    }

    pub(super) fn lower_all_enums(
        &mut self,
        ast_defs: impl ExactSizeIterator<Item = ItemAndInfo<'ast, ast::Enum>>,
    ) -> Result<Vec<EnumDef>, ()> {
        self.lower_all(ast_defs, Self::lower_enum)
    }
    pub(super) fn lower_all_structs(
        &mut self,
        ast_defs: impl ExactSizeIterator<Item = ItemAndInfo<'ast, ast::Struct>>,
    ) -> Result<Vec<StructDef>, ()> {
        self.lower_all(ast_defs, Self::lower_struct)
    }
    pub(super) fn lower_all_out_structs(
        &mut self,
        ast_defs: impl ExactSizeIterator<Item = ItemAndInfo<'ast, ast::Struct>>,
    ) -> Result<Vec<OutStructDef>, ()> {
        self.lower_all(ast_defs, Self::lower_out_struct)
    }
    pub(super) fn lower_all_opaques(
        &mut self,
        ast_defs: impl ExactSizeIterator<Item = ItemAndInfo<'ast, ast::OpaqueStruct>>,
    ) -> Result<Vec<OpaqueDef>, ()> {
        self.lower_all(ast_defs, Self::lower_opaque)
    }

    fn lower_enum(&mut self, item: ItemAndInfo<'ast, ast::Enum>) -> Result<EnumDef, ()> {
        let ast_enum = item.item;
        self.errors.set_item(ast_enum.name.as_str());
        let name = self.lower_ident(&ast_enum.name, "enum name");
        let attrs = self.attr_validator.attr_from_ast(
            &ast_enum.attrs,
            &item.ty_parent_attrs,
            &mut self.errors,
        );

        let mut variants = Ok(Vec::with_capacity(ast_enum.variants.len()));
        let variant_parent_attrs = attrs.for_inheritance(AttrInheritContext::Variant);
        for (ident, discriminant, docs, attrs) in ast_enum.variants.iter() {
            let name = self.lower_ident(ident, "enum variant");
            let attrs =
                self.attr_validator
                    .attr_from_ast(attrs, &variant_parent_attrs, &mut self.errors);
            match (name, &mut variants) {
                (Ok(name), Ok(variants)) => {
                    let variant = EnumVariant {
                        docs: docs.clone(),
                        name,
                        discriminant: *discriminant,
                        attrs,
                    };
                    self.attr_validator.validate(
                        &variant.attrs,
                        AttributeContext::EnumVariant(&variant),
                        &mut self.errors,
                    );
                    variants.push(variant);
                }
                _ => variants = Err(()),
            }
        }

        let mut special_method_presence = SpecialMethodPresence::default();
        let methods = if attrs.disable {
            Vec::new()
        } else {
            self.lower_all_methods(
                &ast_enum.methods[..],
                item.in_path,
                &item.method_parent_attrs,
                item.id,
                &mut special_method_presence,
            )?
        };

        let def = EnumDef::new(
            ast_enum.docs.clone(),
            name?,
            variants?,
            methods,
            attrs,
            special_method_presence,
        );

        self.attr_validator.validate(
            &def.attrs,
            AttributeContext::Type(TypeDef::from(&def)),
            &mut self.errors,
        );

        Ok(def)
    }

    fn lower_opaque(
        &mut self,
        item: ItemAndInfo<'ast, ast::OpaqueStruct>,
    ) -> Result<OpaqueDef, ()> {
        let ast_opaque = item.item;
        self.errors.set_item(ast_opaque.name.as_str());
        let name = self.lower_ident(&ast_opaque.name, "opaque name");

        let attrs = self.attr_validator.attr_from_ast(
            &ast_opaque.attrs,
            &item.ty_parent_attrs,
            &mut self.errors,
        );
        let mut special_method_presence = SpecialMethodPresence::default();
        let methods = if attrs.disable {
            Vec::new()
        } else {
            self.lower_all_methods(
                &ast_opaque.methods[..],
                item.in_path,
                &item.method_parent_attrs,
                item.id,
                &mut special_method_presence,
            )?
        };
        let lifetimes = self.lower_type_lifetime_env(&ast_opaque.lifetimes);

        let def = OpaqueDef::new(
            ast_opaque.docs.clone(),
            name?,
            methods,
            attrs,
            lifetimes?,
            special_method_presence,
        );
        self.attr_validator.validate(
            &def.attrs,
            AttributeContext::Type(TypeDef::from(&def)),
            &mut self.errors,
        );
        Ok(def)
    }

    fn lower_struct(&mut self, item: ItemAndInfo<'ast, ast::Struct>) -> Result<StructDef, ()> {
        let ast_struct = item.item;
        self.errors.set_item(ast_struct.name.as_str());
        let name = self.lower_ident(&ast_struct.name, "struct name");

        let fields = if ast_struct.fields.is_empty() {
            self.errors.push(LoweringError::Other(format!(
                "struct `{}` is a ZST because it has no fields",
                ast_struct.name
            )));
            Err(())
        } else {
            let mut fields = Ok(Vec::with_capacity(ast_struct.fields.len()));

            for (name, ty, docs) in ast_struct.fields.iter() {
                let name = self.lower_ident(name, "struct field name");
                let ty = self.lower_type(ty, &mut &ast_struct.lifetimes, item.in_path);

                match (name, ty, &mut fields) {
                    (Ok(name), Ok(ty), Ok(fields)) => fields.push(StructField {
                        docs: docs.clone(),
                        name,
                        ty,
                    }),
                    _ => fields = Err(()),
                }
            }

            fields
        };
        let attrs = self.attr_validator.attr_from_ast(
            &ast_struct.attrs,
            &item.ty_parent_attrs,
            &mut self.errors,
        );
        let lifetimes = self.lower_type_lifetime_env(&ast_struct.lifetimes);

        let mut special_method_presence = SpecialMethodPresence::default();
        let methods = if attrs.disable {
            Vec::new()
        } else {
            self.lower_all_methods(
                &ast_struct.methods[..],
                item.in_path,
                &item.method_parent_attrs,
                item.id,
                &mut special_method_presence,
            )?
        };
        let def = StructDef::new(
            ast_struct.docs.clone(),
            name?,
            fields?,
            methods,
            attrs,
            lifetimes?,
            special_method_presence,
        );

        self.attr_validator.validate(
            &def.attrs,
            AttributeContext::Type(TypeDef::from(&def)),
            &mut self.errors,
        );
        Ok(def)
    }

    fn lower_out_struct(
        &mut self,
        item: ItemAndInfo<'ast, ast::Struct>,
    ) -> Result<OutStructDef, ()> {
        let ast_out_struct = item.item;
        self.errors.set_item(ast_out_struct.name.as_str());
        let name = self.lower_ident(&ast_out_struct.name, "out-struct name");

        let fields = if ast_out_struct.fields.is_empty() {
            self.errors.push(LoweringError::Other(format!(
                "struct `{}` is a ZST because it has no fields",
                ast_out_struct.name
            )));
            Err(())
        } else {
            let mut fields = Ok(Vec::with_capacity(ast_out_struct.fields.len()));

            for (name, ty, docs) in ast_out_struct.fields.iter() {
                let name = self.lower_ident(name, "out-struct field name");
                let ty =
                    self.lower_out_type(ty, &mut &ast_out_struct.lifetimes, item.in_path, true);

                match (name, ty, &mut fields) {
                    (Ok(name), Ok(ty), Ok(fields)) => fields.push(OutStructField {
                        docs: docs.clone(),
                        name,
                        ty,
                    }),
                    _ => fields = Err(()),
                }
            }

            fields
        };
        let attrs = self.attr_validator.attr_from_ast(
            &ast_out_struct.attrs,
            &item.ty_parent_attrs,
            &mut self.errors,
        );
        let mut special_method_presence = SpecialMethodPresence::default();
        let methods = if attrs.disable {
            Vec::new()
        } else {
            self.lower_all_methods(
                &ast_out_struct.methods[..],
                item.in_path,
                &item.method_parent_attrs,
                item.id,
                &mut special_method_presence,
            )?
        };

        let lifetimes = self.lower_type_lifetime_env(&ast_out_struct.lifetimes);
        let def = OutStructDef::new(
            ast_out_struct.docs.clone(),
            name?,
            fields?,
            methods,
            attrs,
            lifetimes?,
            special_method_presence,
        );

        self.attr_validator.validate(
            &def.attrs,
            AttributeContext::Type(TypeDef::from(&def)),
            &mut self.errors,
        );
        Ok(def)
    }

    /// Lowers an [`ast::Method`]s an [`hir::Method`].
    ///
    /// If there are any errors, they're pushed to `errors` and `None` is returned.
    fn lower_method(
        &mut self,
        method: &'ast ast::Method,
        in_path: &ast::Path,
        method_parent_attrs: &Attrs,
        self_id: TypeId,
        special_method_presence: &mut SpecialMethodPresence,
    ) -> Result<Method, ()> {
        self.errors.set_subitem(method.name.as_str());
        let name = self.lower_ident(&method.name, "method name");

        let (ast_params, takes_writeable) = match method.params.split_last() {
            Some((last, remaining)) if last.is_writeable() => (remaining, true),
            _ => (&method.params[..], false),
        };

        let self_param_ltl = SelfParamLifetimeLowerer::new(&method.lifetime_env, self)?;

        let (param_self, param_ltl) = if let Some(self_param) = method.self_param.as_ref() {
            let (param_self, param_ltl) =
                self.lower_self_param(self_param, self_param_ltl, &method.full_path_name, in_path)?;
            (Some(param_self), param_ltl)
        } else {
            (None, SelfParamLifetimeLowerer::no_self_ref(self_param_ltl))
        };

        let (params, return_ltl) = self.lower_many_params(ast_params, param_ltl, in_path)?;

        let (output, lifetime_env) = self.lower_return_type(
            method.return_type.as_ref(),
            takes_writeable,
            return_ltl,
            in_path,
        )?;

        let attrs =
            self.attr_validator
                .attr_from_ast(&method.attrs, method_parent_attrs, &mut self.errors);

        let hir_method = Method {
            docs: method.docs.clone(),
            name: name?,
            lifetime_env,
            param_self,
            params,
            output,
            attrs,
        };

        self.attr_validator.validate(
            &hir_method.attrs,
            AttributeContext::Method(&hir_method, self_id, special_method_presence),
            &mut self.errors,
        );

        let is_comparison = matches!(
            hir_method.attrs.special_method,
            Some(SpecialMethod::Comparison)
        );
        if is_comparison && method.return_type != Some(ast::TypeName::Ordering) {
            self.errors.push(LoweringError::Other(
                "Found comparison method that does not return cmp::Ordering".into(),
            ));
            return Err(());
        }

        Ok(hir_method)
    }

    /// Lowers many [`ast::Method`]s into a vector of [`hir::Method`]s.
    ///
    /// If there are any errors, they're pushed to `errors` and `None` is returned.
    fn lower_all_methods(
        &mut self,
        ast_methods: &'ast [ast::Method],
        in_path: &ast::Path,
        method_parent_attrs: &Attrs,
        self_id: TypeId,
        special_method_presence: &mut SpecialMethodPresence,
    ) -> Result<Vec<Method>, ()> {
        let mut methods = Ok(Vec::with_capacity(ast_methods.len()));

        for method in ast_methods {
            let method = self.lower_method(
                method,
                in_path,
                method_parent_attrs,
                self_id,
                special_method_presence,
            );
            match (method, &mut methods) {
                (Ok(method), Ok(methods)) => {
                    methods.push(method);
                }
                _ => methods = Err(()),
            }
        }

        methods
    }

    /// Lowers an [`ast::TypeName`]s into a [`hir::Type`].
    ///
    /// If there are any errors, they're pushed to `errors` and `None` is returned.
    fn lower_type(
        &mut self,
        ty: &ast::TypeName,
        ltl: &mut impl LifetimeLowerer,
        in_path: &ast::Path,
    ) -> Result<Type, ()> {
        match ty {
            ast::TypeName::Primitive(prim) => Ok(Type::Primitive(PrimitiveType::from_ast(*prim))),
            ast::TypeName::Ordering => {
                self.errors.push(LoweringError::Other("Found cmp::Ordering in parameter or struct field, it is only allowed in return types".to_string()));
                Err(())
            }
            ast::TypeName::Named(path) | ast::TypeName::SelfType(path) => {
                match path.resolve(in_path, self.env) {
                    ast::CustomType::Struct(strct) => {
                        if let Some(tcx_id) = self.lookup_id.resolve_struct(strct) {
                            let lifetimes = ltl.lower_generics(
                                &path.lifetimes[..],
                                &strct.lifetimes,
                                ty.is_self(),
                            );

                            Ok(Type::Struct(StructPath::new(lifetimes, tcx_id)))
                        } else if self.lookup_id.resolve_out_struct(strct).is_some() {
                            self.errors.push(LoweringError::Other(format!("found struct in input that is marked with #[diplomat::out]: {ty} in {path}")));
                            Err(())
                        } else {
                            unreachable!("struct `{}` wasn't found in the set of structs or out-structs, this is a bug.", strct.name);
                        }
                    }
                    ast::CustomType::Opaque(_) => {
                        self.errors.push(LoweringError::Other(format!(
                            "Opaque passed by value: {path}"
                        )));
                        Err(())
                    }
                    ast::CustomType::Enum(enm) => {
                        let tcx_id = self.lookup_id.resolve_enum(enm).expect(
                            "can't find enum in lookup map, which contains all enums from env",
                        );

                        Ok(Type::Enum(EnumPath::new(tcx_id)))
                    }
                }
            }
            ast::TypeName::Reference(lifetime, mutability, ref_ty) => match ref_ty.as_ref() {
                ast::TypeName::Named(path) | ast::TypeName::SelfType(path) => {
                    match path.resolve(in_path, self.env) {
                        ast::CustomType::Opaque(opaque) => {
                            let borrow = Borrow::new(ltl.lower_lifetime(lifetime), *mutability);
                            let lifetimes = ltl.lower_generics(
                                &path.lifetimes[..],
                                &opaque.lifetimes,
                                ref_ty.is_self(),
                            );
                            let tcx_id = self.lookup_id.resolve_opaque(opaque).expect(
                            "can't find opaque in lookup map, which contains all opaques from env",
                        );

                            Ok(Type::Opaque(OpaquePath::new(
                                lifetimes,
                                Optional(false),
                                borrow,
                                tcx_id,
                            )))
                        }
                        _ => {
                            self.errors.push(LoweringError::Other(format!("found &T in input where T is a custom type, but not opaque. T = {ref_ty}")));
                            Err(())
                        }
                    }
                }
                _ => {
                    self.errors.push(LoweringError::Other(format!("found &T in input where T isn't a custom type and therefore not opaque. T = {ref_ty}")));
                    Err(())
                }
            },
            ast::TypeName::Box(box_ty) => {
                self.errors.push(match box_ty.as_ref() {
                ast::TypeName::Named(path) | ast::TypeName::SelfType(path) => {
                    match path.resolve(in_path, self.env) {
                        ast::CustomType::Opaque(_) => LoweringError::Other(format!("found Box<T> in input where T is an opaque, but owned opaques aren't allowed in inputs. try &T instead? T = {path}")),
                        _ => LoweringError::Other(format!("found Box<T> in input where T is a custom type but not opaque. non-opaques can't be behind pointers, and opaques in inputs can't be owned. T = {path}")),
                    }
                }
                _ => LoweringError::Other(format!("found Box<T> in input where T isn't a custom type. T = {box_ty}")),
            });
                Err(())
            }
            ast::TypeName::Option(opt_ty) => {
                match opt_ty.as_ref() {
                    ast::TypeName::Reference(lifetime, mutability, ref_ty) => match ref_ty.as_ref()
                    {
                        ast::TypeName::Named(path) | ast::TypeName::SelfType(path) => match path
                            .resolve(in_path, self.env)
                        {
                            ast::CustomType::Opaque(opaque) => {
                                let borrow = Borrow::new(ltl.lower_lifetime(lifetime), *mutability);
                                let lifetimes = ltl.lower_generics(
                                    &path.lifetimes,
                                    &opaque.lifetimes,
                                    ref_ty.is_self(),
                                );
                                let tcx_id = self.lookup_id.resolve_opaque(opaque).expect(
                                    "can't find opaque in lookup map, which contains all opaques from env",
                                );

                                Ok(Type::Opaque(OpaquePath::new(
                                    lifetimes,
                                    Optional(true),
                                    borrow,
                                    tcx_id,
                                )))
                            }
                            _ => {
                                self.errors.push(LoweringError::Other(format!("found Option<&T> in input where T is a custom type, but it's not opaque. T = {ref_ty}")));
                                Err(())
                            }
                        },
                        _ => {
                            self.errors.push(LoweringError::Other(format!("found Option<&T> in input, but T isn't a custom type and therefore not opaque. T = {ref_ty}")));
                            Err(())
                        }
                    },
                    ast::TypeName::Box(box_ty) => {
                        // we could see whats in the box here too
                        self.errors.push(LoweringError::Other(format!("found Option<Box<T>> in input, but box isn't allowed in inputs. T = {box_ty}")));
                        Err(())
                    }
                    _ => {
                        self.errors.push(LoweringError::Other(format!("found Option<T> in input, where T isn't a reference but Option<T> in inputs requires that T is a reference to an opaque. T = {opt_ty}")));
                        Err(())
                    }
                }
            }
            ast::TypeName::Result(_, _, _) => {
                self.errors.push(LoweringError::Other(
                    "Results can only appear as the top-level return type of methods".into(),
                ));
                Err(())
            }
            ast::TypeName::Writeable => {
                self.errors.push(LoweringError::Other(
                    "Writeables can only appear as the last parameter of a method".into(),
                ));
                Err(())
            }
            ast::TypeName::StrReference(lifetime, encoding) => Ok(Type::Slice(Slice::Str(
                lifetime.as_ref().map(|lt| ltl.lower_lifetime(lt)),
                *encoding,
            ))),
            ast::TypeName::StrSlice(encoding) => Ok(Type::Slice(Slice::Strs(*encoding))),
            ast::TypeName::PrimitiveSlice(lm, prim) => Ok(Type::Slice(Slice::Primitive(
                lm.as_ref()
                    .map(|(lt, m)| Borrow::new(ltl.lower_lifetime(lt), *m)),
                PrimitiveType::from_ast(*prim),
            ))),
            ast::TypeName::Unit => {
                self.errors.push(LoweringError::Other("Unit types can only appear as the return value of a method, or as the Ok/Err variants of a returned result".into()));
                Err(())
            }
        }
    }

    /// Lowers an [`ast::TypeName`]s into an [`hir::OutType`].
    ///
    /// If there are any errors, they're pushed to `errors` and `None` is returned.
    fn lower_out_type(
        &mut self,
        ty: &ast::TypeName,
        ltl: &mut impl LifetimeLowerer,
        in_path: &ast::Path,
        in_struct: bool,
    ) -> Result<OutType, ()> {
        match ty {
            ast::TypeName::Primitive(prim) => {
                Ok(OutType::Primitive(PrimitiveType::from_ast(*prim)))
            }
            ast::TypeName::Ordering => {
                if in_struct {
                    self.errors.push(LoweringError::Other(
                        "Found cmp::Ordering in struct field, it is only allowed in return types"
                            .to_string(),
                    ));
                    Err(())
                } else {
                    Ok(Type::Primitive(PrimitiveType::Int(IntType::I8)))
                }
            }
            ast::TypeName::Named(path) | ast::TypeName::SelfType(path) => {
                match path.resolve(in_path, self.env) {
                    ast::CustomType::Struct(strct) => {
                        let lifetimes =
                            ltl.lower_generics(&path.lifetimes, &strct.lifetimes, ty.is_self());

                        if let Some(tcx_id) = self.lookup_id.resolve_struct(strct) {
                            Ok(OutType::Struct(ReturnableStructPath::Struct(
                                StructPath::new(lifetimes, tcx_id),
                            )))
                        } else if let Some(tcx_id) = self.lookup_id.resolve_out_struct(strct) {
                            Ok(OutType::Struct(ReturnableStructPath::OutStruct(
                                OutStructPath::new(lifetimes, tcx_id),
                            )))
                        } else {
                            unreachable!("struct `{}` wasn't found in the set of structs or out-structs, this is a bug.", strct.name);
                        }
                    }
                    ast::CustomType::Opaque(_) => {
                        self.errors.push(LoweringError::Other(format!(
                            "Opaque passed by value in input: {path}"
                        )));
                        Err(())
                    }
                    ast::CustomType::Enum(enm) => {
                        let tcx_id = self.lookup_id.resolve_enum(enm).expect(
                            "can't find enum in lookup map, which contains all enums from env",
                        );

                        Ok(OutType::Enum(EnumPath::new(tcx_id)))
                    }
                }
            }
            ast::TypeName::Reference(lifetime, mutability, ref_ty) => match ref_ty.as_ref() {
                ast::TypeName::Named(path) | ast::TypeName::SelfType(path) => {
                    match path.resolve(in_path, self.env) {
                        ast::CustomType::Opaque(opaque) => {
                            let borrow = Borrow::new(ltl.lower_lifetime(lifetime), *mutability);
                            let lifetimes = ltl.lower_generics(
                                &path.lifetimes,
                                &opaque.lifetimes,
                                ref_ty.is_self(),
                            );
                            let tcx_id = self.lookup_id.resolve_opaque(opaque).expect(
                            "can't find opaque in lookup map, which contains all opaques from env",
                        );

                            Ok(OutType::Opaque(OpaquePath::new(
                                lifetimes,
                                Optional(false),
                                MaybeOwn::Borrow(borrow),
                                tcx_id,
                            )))
                        }
                        _ => {
                            self.errors.push(LoweringError::Other(format!("found &T in output where T is a custom type, but not opaque. T = {ref_ty}")));
                            Err(())
                        }
                    }
                }
                _ => {
                    self.errors.push(LoweringError::Other(format!("found &T in output where T isn't a custom type and therefore not opaque. T = {ref_ty}")));
                    Err(())
                }
            },
            ast::TypeName::Box(box_ty) => match box_ty.as_ref() {
                ast::TypeName::Named(path) | ast::TypeName::SelfType(path) => {
                    match path.resolve(in_path, self.env) {
                        ast::CustomType::Opaque(opaque) => {
                            let lifetimes = ltl.lower_generics(
                                &path.lifetimes,
                                &opaque.lifetimes,
                                box_ty.is_self(),
                            );
                            let tcx_id = self.lookup_id.resolve_opaque(opaque).expect(
                            "can't find opaque in lookup map, which contains all opaques from env",
                        );

                            Ok(OutType::Opaque(OpaquePath::new(
                                lifetimes,
                                Optional(false),
                                MaybeOwn::Own,
                                tcx_id,
                            )))
                        }
                        _ => {
                            self.errors.push(LoweringError::Other(format!("found Box<T> in output where T is a custom type but not opaque. non-opaques can't be behind pointers. T = {path}")));
                            Err(())
                        }
                    }
                }
                _ => {
                    self.errors.push(LoweringError::Other(format!(
                        "found Box<T> in output where T isn't a custom type. T = {box_ty}"
                    )));
                    Err(())
                }
            },
            ast::TypeName::Option(opt_ty) => match opt_ty.as_ref() {
                ast::TypeName::Reference(lifetime, mutability, ref_ty) => match ref_ty.as_ref() {
                    ast::TypeName::Named(path) | ast::TypeName::SelfType(path) => {
                        match path.resolve(in_path, self.env) {
                            ast::CustomType::Opaque(opaque) => {
                                let borrow = Borrow::new(ltl.lower_lifetime(lifetime), *mutability);
                                let lifetimes = ltl.lower_generics(
                                    &path.lifetimes,
                                    &opaque.lifetimes,
                                    ref_ty.is_self(),
                                );
                                let tcx_id = self.lookup_id.resolve_opaque(opaque).expect(
                                "can't find opaque in lookup map, which contains all opaques from env",
                            );

                                Ok(OutType::Opaque(OpaquePath::new(
                                    lifetimes,
                                    Optional(true),
                                    MaybeOwn::Borrow(borrow),
                                    tcx_id,
                                )))
                            }
                            _ => {
                                self.errors.push(LoweringError::Other(format!("found Option<&T> where T is a custom type, but it's not opaque. T = {ref_ty}")));
                                Err(())
                            }
                        }
                    }
                    _ => {
                        self.errors.push(LoweringError::Other(format!("found Option<&T>, but T isn't a custom type and therefore not opaque. T = {ref_ty}")));
                        Err(())
                    }
                },
                ast::TypeName::Box(box_ty) => match box_ty.as_ref() {
                    ast::TypeName::Named(path) | ast::TypeName::SelfType(path) => {
                        match path.resolve(in_path, self.env) {
                            ast::CustomType::Opaque(opaque) => {
                                let lifetimes = ltl.lower_generics(
                                    &path.lifetimes,
                                    &opaque.lifetimes,
                                    box_ty.is_self(),
                                );
                                let tcx_id = self.lookup_id.resolve_opaque(opaque).expect(
                            "can't find opaque in lookup map, which contains all opaques from env",
                        );

                                Ok(OutType::Opaque(OpaquePath::new(
                                    lifetimes,
                                    Optional(true),
                                    MaybeOwn::Own,
                                    tcx_id,
                                )))
                            }
                            _ => {
                                self.errors.push(LoweringError::Other(format!("found Option<Box<T>> where T is a custom type, but it's not opaque. T = {box_ty}")));
                                Err(())
                            }
                        }
                    }
                    _ => {
                        self.errors.push(LoweringError::Other(format!("found Option<Box<T>>, but T isn't a custom type and therefore not opaque. T = {box_ty}")));
                        Err(())
                    }
                },
                _ => {
                    self.errors.push(LoweringError::Other(format!("found Option<T>, where T isn't a reference but Option<T> requires that T is a reference to an opaque. T = {opt_ty}")));
                    Err(())
                }
            },
            ast::TypeName::Result(_, _, _) => {
                self.errors.push(LoweringError::Other(
                    "Results can only appear as the top-level return type of methods".into(),
                ));
                Err(())
            }
            ast::TypeName::Writeable => {
                self.errors.push(LoweringError::Other(
                    "Writeables can only appear as the last parameter of a method".into(),
                ));
                Err(())
            }
            ast::TypeName::StrReference(lifetime, encoding) => Ok(OutType::Slice(Slice::Str(
                lifetime.as_ref().map(|l| ltl.lower_lifetime(l)),
                *encoding,
            ))),
            ast::TypeName::StrSlice(..) => {
                self.errors.push(LoweringError::Other(
                    "String slices can only be an input type".into(),
                ));
                Err(())
            }
            ast::TypeName::PrimitiveSlice(lm, prim) => Ok(OutType::Slice(Slice::Primitive(
                lm.as_ref()
                    .map(|(lt, m)| Borrow::new(ltl.lower_lifetime(lt), *m)),
                PrimitiveType::from_ast(*prim),
            ))),
            ast::TypeName::Unit => {
                self.errors.push(LoweringError::Other("Unit types can only appear as the return value of a method, or as the Ok/Err variants of a returned result".into()));
                Err(())
            }
        }
    }

    /// Lowers an [`ast::SelfParam`] into an [`hir::ParamSelf`].
    ///
    /// If there are any errors, they're pushed to `errors` and `None` is returned.
    fn lower_self_param(
        &mut self,
        self_param: &ast::SelfParam,
        self_param_ltl: SelfParamLifetimeLowerer<'ast>,
        method_full_path: &ast::Ident, // for better error msg
        in_path: &ast::Path,
    ) -> Result<(ParamSelf, ParamLifetimeLowerer<'ast>), ()> {
        match self_param.path_type.resolve(in_path, self.env) {
            ast::CustomType::Struct(strct) => {
                if let Some(tcx_id) = self.lookup_id.resolve_struct(strct) {
                    if self_param.reference.is_some() {
                        self.errors.push(LoweringError::Other(format!("Method `{method_full_path}` takes a reference to a struct as a self parameter, which isn't allowed")));
                        Err(())
                    } else {
                        let mut param_ltl = self_param_ltl.no_self_ref();

                        // Even if we explicitly write out the type of `self` like
                        // `self: Foo<'a>`, the `'a` is still not considered for
                        // elision according to rustc, so is_self=true.
                        let type_lifetimes = param_ltl.lower_generics(
                            &self_param.path_type.lifetimes[..],
                            &strct.lifetimes,
                            true,
                        );

                        Ok((
                            ParamSelf::new(SelfType::Struct(StructPath::new(
                                type_lifetimes,
                                tcx_id,
                            ))),
                            param_ltl,
                        ))
                    }
                } else if self.lookup_id.resolve_out_struct(strct).is_some() {
                    if let Some((lifetime, _)) = &self_param.reference {
                        self.errors.push(LoweringError::Other(format!("Method `{method_full_path}` takes an out-struct as the self parameter, which isn't allowed. Also, it's behind a reference, `{lifetime}`, but only opaques can be behind references")));
                        Err(())
                    } else {
                        self.errors.push(LoweringError::Other(format!("Method `{method_full_path}` takes an out-struct as the self parameter, which isn't allowed")));
                        Err(())
                    }
                } else {
                    unreachable!(
                    "struct `{}` wasn't found in the set of structs or out-structs, this is a bug.",
                    strct.name
                );
                }
            }
            ast::CustomType::Opaque(opaque) => {
                let tcx_id = self
                    .lookup_id
                    .resolve_opaque(opaque)
                    .expect("opaque is in env");

                if let Some((lifetime, mutability)) = &self_param.reference {
                    let (borrow_lifetime, mut param_ltl) = self_param_ltl.lower_self_ref(lifetime);
                    let borrow = Borrow::new(borrow_lifetime, *mutability);
                    let lifetimes = param_ltl.lower_generics(
                        &self_param.path_type.lifetimes,
                        &opaque.lifetimes,
                        true,
                    );

                    Ok((
                        ParamSelf::new(SelfType::Opaque(OpaquePath::new(
                            lifetimes,
                            NonOptional,
                            borrow,
                            tcx_id,
                        ))),
                        param_ltl,
                    ))
                } else {
                    self.errors.push(LoweringError::Other(format!("Method `{method_full_path}` takes an opaque by value as the self parameter, but opaques as inputs must be behind refs")));
                    Err(())
                }
            }
            ast::CustomType::Enum(enm) => {
                let tcx_id = self.lookup_id.resolve_enum(enm).expect("enum is in env");

                Ok((
                    ParamSelf::new(SelfType::Enum(EnumPath::new(tcx_id))),
                    self_param_ltl.no_self_ref(),
                ))
            }
        }
    }

    /// Lowers an [`ast::Param`] into an [`hir::Param`].
    ///
    /// If there are any errors, they're pushed to `errors` and `None` is returned.
    ///
    /// Note that this expects that if there was a writeable param at the end in
    /// the method, it's not passed into here.
    fn lower_param(
        &mut self,
        param: &ast::Param,
        ltl: &mut impl LifetimeLowerer,
        in_path: &ast::Path,
    ) -> Result<Param, ()> {
        let name = self.lower_ident(¶m.name, "param name");
        let ty = self.lower_type(¶m.ty, ltl, in_path);

        Ok(Param::new(name?, ty?))
    }

    /// Lowers many [`ast::Param`]s into a vector of [`hir::Param`]s.
    ///
    /// If there are any errors, they're pushed to `errors` and `None` is returned.
    ///
    /// Note that this expects that if there was a writeable param at the end in
    /// the method, `ast_params` was sliced to not include it. This happens in
    /// `self.lower_method`, the caller of this function.
    fn lower_many_params(
        &mut self,
        ast_params: &[ast::Param],
        mut param_ltl: ParamLifetimeLowerer<'ast>,
        in_path: &ast::Path,
    ) -> Result<(Vec<Param>, ReturnLifetimeLowerer<'ast>), ()> {
        let mut params = Ok(Vec::with_capacity(ast_params.len()));

        for param in ast_params {
            let param = self.lower_param(param, &mut param_ltl, in_path);

            match (param, &mut params) {
                (Ok(param), Ok(params)) => {
                    params.push(param);
                }
                _ => params = Err(()),
            }
        }

        Ok((params?, param_ltl.into_return_ltl()))
    }

    /// Lowers the return type of an [`ast::Method`] into a [`hir::ReturnFallability`].
    ///
    /// If there are any errors, they're pushed to `errors` and `None` is returned.
    fn lower_return_type(
        &mut self,
        return_type: Option<&ast::TypeName>,
        takes_writeable: bool,
        mut return_ltl: ReturnLifetimeLowerer<'_>,
        in_path: &ast::Path,
    ) -> Result<(ReturnType, LifetimeEnv), ()> {
        let writeable_or_unit = if takes_writeable {
            SuccessType::Writeable
        } else {
            SuccessType::Unit
        };
        match return_type.unwrap_or(&ast::TypeName::Unit) {
            ast::TypeName::Result(ok_ty, err_ty, _) => {
                let ok_ty = match ok_ty.as_ref() {
                    ast::TypeName::Unit => Ok(writeable_or_unit),
                    ty => self
                        .lower_out_type(ty, &mut return_ltl, in_path, false)
                        .map(SuccessType::OutType),
                };
                let err_ty = match err_ty.as_ref() {
                    ast::TypeName::Unit => Ok(None),
                    ty => self
                        .lower_out_type(ty, &mut return_ltl, in_path, false)
                        .map(Some),
                };

                match (ok_ty, err_ty) {
                    (Ok(ok_ty), Ok(err_ty)) => Ok(ReturnType::Fallible(ok_ty, err_ty)),
                    _ => Err(()),
                }
            }
            ty @ ast::TypeName::Option(value_ty) => match &**value_ty {
                ast::TypeName::Box(..) | ast::TypeName::Reference(..) => self
                    .lower_out_type(ty, &mut return_ltl, in_path, false)
                    .map(SuccessType::OutType)
                    .map(ReturnType::Infallible),
                _ => self
                    .lower_out_type(value_ty, &mut return_ltl, in_path, false)
                    .map(SuccessType::OutType)
                    .map(ReturnType::Nullable),
            },
            ast::TypeName::Unit => Ok(ReturnType::Infallible(writeable_or_unit)),
            ty => self
                .lower_out_type(ty, &mut return_ltl, in_path, false)
                .map(|ty| ReturnType::Infallible(SuccessType::OutType(ty))),
        }
        .map(|r_ty| (r_ty, return_ltl.finish()))
    }

    fn lower_named_lifetime(
        &mut self,
        lifetime: &ast::lifetimes::LifetimeNode,
    ) -> Result<BoundedLifetime, ()> {
        Ok(BoundedLifetime {
            ident: self.lower_ident(lifetime.lifetime.name(), "lifetime")?,
            longer: lifetime.longer.iter().copied().map(Lifetime::new).collect(),
            shorter: lifetime
                .shorter
                .iter()
                .copied()
                .map(Lifetime::new)
                .collect(),
        })
    }

    /// Lowers a lifetime env found on a type
    ///
    /// Should not be extended to return LifetimeEnv<Method>, which needs to use the lifetime
    /// lowerers to handle elision.
    fn lower_type_lifetime_env(&mut self, ast: &ast::LifetimeEnv) -> Result<LifetimeEnv, ()> {
        let nodes = ast
            .nodes
            .iter()
            .map(|lt| self.lower_named_lifetime(lt))
            .collect::<Result<_, ()>>()?;

        Ok(LifetimeEnv::new(nodes, ast.nodes.len()))
    }
}

[ zur Elbe Produktseite wechseln0.59Quellennavigators  ]

                                                                                                                                                                                                                                                                                                                                                                                                     


Neuigkeiten

     Aktuelles
     Motto des Tages

Software

     Produkte
     Quellcodebibliothek

Aktivitäten

     Artikel über Sicherheit
     Anleitung zur Aktivierung von SSL

Muße

     Gedichte
     Musik
     Bilder

Jenseits des Üblichen ....

Besucherstatistik

Besucherstatistik

Monitoring

Montastic status badge