fix: Fix attributes on generic parameters colliding in item tree

This commit is contained in:
Lukas Wirth 2024-04-27 12:33:04 +02:00
parent 2668912688
commit bfe59bbdc8
11 changed files with 248 additions and 210 deletions

View file

@ -698,24 +698,22 @@ impl<'a> AssocItemCollector<'a> {
match item { match item {
AssocItem::Function(id) => { AssocItem::Function(id) => {
let item = &item_tree[id]; let item = &item_tree[id];
let def = let def =
FunctionLoc { container, id: ItemTreeId::new(tree_id, id) }.intern(self.db); FunctionLoc { container, id: ItemTreeId::new(tree_id, id) }.intern(self.db);
self.items.push((item.name.clone(), def.into())); self.items.push((item.name.clone(), def.into()));
} }
AssocItem::TypeAlias(id) => {
let item = &item_tree[id];
let def =
TypeAliasLoc { container, id: ItemTreeId::new(tree_id, id) }.intern(self.db);
self.items.push((item.name.clone(), def.into()));
}
AssocItem::Const(id) => { AssocItem::Const(id) => {
let item = &item_tree[id]; let item = &item_tree[id];
let Some(name) = item.name.clone() else { return }; let Some(name) = item.name.clone() else { return };
let def = ConstLoc { container, id: ItemTreeId::new(tree_id, id) }.intern(self.db); let def = ConstLoc { container, id: ItemTreeId::new(tree_id, id) }.intern(self.db);
self.items.push((name, def.into())); self.items.push((name, def.into()));
} }
AssocItem::TypeAlias(id) => {
let item = &item_tree[id];
let def =
TypeAliasLoc { container, id: ItemTreeId::new(tree_id, id) }.intern(self.db);
self.items.push((item.name.clone(), def.into()));
}
AssocItem::MacroCall(call) => { AssocItem::MacroCall(call) => {
let file_id = self.expander.current_file_id(); let file_id = self.expander.current_file_id();
let MacroCall { ast_id, expand_to, ctxt, ref path } = item_tree[call]; let MacroCall { ast_id, expand_to, ctxt, ref path } = item_tree[call];

View file

@ -1177,6 +1177,8 @@ pub mod fmt {
//- /main.rs crate:main deps:alloc,std //- /main.rs crate:main deps:alloc,std
#![no_std] #![no_std]
extern crate alloc;
$0 $0
//- /std.rs crate:std deps:alloc //- /std.rs crate:std deps:alloc

View file

@ -20,7 +20,7 @@ use triomphe::Arc;
use crate::{ use crate::{
db::DefDatabase, db::DefDatabase,
expander::Expander, expander::Expander,
item_tree::{GenericsItemTreeNode, ItemTree}, item_tree::{AttrOwner, FileItemTreeId, GenericModItem, GenericsItemTreeNode, ItemTree},
lower::LowerCtx, lower::LowerCtx,
nameres::{DefMap, MacroSubNs}, nameres::{DefMap, MacroSubNs},
type_ref::{ConstRef, LifetimeRef, TypeBound, TypeRef}, type_ref::{ConstRef, LifetimeRef, TypeBound, TypeRef},
@ -456,56 +456,67 @@ impl GenericParams {
let cfg_options = &cfg_options[krate].cfg_options; let cfg_options = &cfg_options[krate].cfg_options;
// Returns the generic parameters that are enabled under the current `#[cfg]` options // Returns the generic parameters that are enabled under the current `#[cfg]` options
let enabled_params = |params: &Interned<GenericParams>, item_tree: &ItemTree| { let enabled_params =
let enabled = |param| item_tree.attrs(db, krate, param).is_cfg_enabled(cfg_options); |params: &Interned<GenericParams>, item_tree: &ItemTree, parent: GenericModItem| {
let enabled = |param| item_tree.attrs(db, krate, param).is_cfg_enabled(cfg_options);
let attr_owner_ct = |param| AttrOwner::TypeOrConstParamData(parent, param);
let attr_owner_lt = |param| AttrOwner::LifetimeParamData(parent, param);
// In the common case, no parameters will by disabled by `#[cfg]` attributes. // In the common case, no parameters will by disabled by `#[cfg]` attributes.
// Therefore, make a first pass to check if all parameters are enabled and, if so, // Therefore, make a first pass to check if all parameters are enabled and, if so,
// clone the `Interned<GenericParams>` instead of recreating an identical copy. // clone the `Interned<GenericParams>` instead of recreating an identical copy.
let all_type_or_consts_enabled = let all_type_or_consts_enabled =
params.type_or_consts.iter().all(|(idx, _)| enabled(idx.into())); params.type_or_consts.iter().all(|(idx, _)| enabled(attr_owner_ct(idx)));
let all_lifetimes_enabled = params.lifetimes.iter().all(|(idx, _)| enabled(idx.into())); let all_lifetimes_enabled =
params.lifetimes.iter().all(|(idx, _)| enabled(attr_owner_lt(idx)));
if all_type_or_consts_enabled && all_lifetimes_enabled { if all_type_or_consts_enabled && all_lifetimes_enabled {
params.clone() params.clone()
} else { } else {
Interned::new(GenericParams { Interned::new(GenericParams {
type_or_consts: all_type_or_consts_enabled type_or_consts: all_type_or_consts_enabled
.then(|| params.type_or_consts.clone()) .then(|| params.type_or_consts.clone())
.unwrap_or_else(|| { .unwrap_or_else(|| {
params params
.type_or_consts .type_or_consts
.iter() .iter()
.filter(|(idx, _)| enabled((*idx).into())) .filter(|&(idx, _)| enabled(attr_owner_ct(idx)))
.map(|(_, param)| param.clone()) .map(|(_, param)| param.clone())
.collect() .collect()
}), }),
lifetimes: all_lifetimes_enabled lifetimes: all_lifetimes_enabled
.then(|| params.lifetimes.clone()) .then(|| params.lifetimes.clone())
.unwrap_or_else(|| { .unwrap_or_else(|| {
params params
.lifetimes .lifetimes
.iter() .iter()
.filter(|(idx, _)| enabled((*idx).into())) .filter(|&(idx, _)| enabled(attr_owner_lt(idx)))
.map(|(_, param)| param.clone()) .map(|(_, param)| param.clone())
.collect() .collect()
}), }),
where_predicates: params.where_predicates.clone(), where_predicates: params.where_predicates.clone(),
}) })
} }
}; };
fn id_to_generics<Id: GenericsItemTreeNode>( fn id_to_generics<Id: GenericsItemTreeNode>(
db: &dyn DefDatabase, db: &dyn DefDatabase,
id: impl for<'db> Lookup< id: impl for<'db> Lookup<
Database<'db> = dyn DefDatabase + 'db, Database<'db> = dyn DefDatabase + 'db,
Data = impl ItemTreeLoc<Id = Id>, Data = impl ItemTreeLoc<Id = Id>,
>, >,
enabled_params: impl Fn(&Interned<GenericParams>, &ItemTree) -> Interned<GenericParams>, enabled_params: impl Fn(
) -> Interned<GenericParams> { &Interned<GenericParams>,
&ItemTree,
GenericModItem,
) -> Interned<GenericParams>,
) -> Interned<GenericParams>
where
FileItemTreeId<Id>: Into<GenericModItem>,
{
let id = id.lookup(db).item_tree_id(); let id = id.lookup(db).item_tree_id();
let tree = id.item_tree(db); let tree = id.item_tree(db);
let item = &tree[id.value]; let item = &tree[id.value];
enabled_params(item.generic_params(), &tree) enabled_params(item.generic_params(), &tree, id.value.into())
} }
match def { match def {
@ -514,7 +525,8 @@ impl GenericParams {
let tree = loc.id.item_tree(db); let tree = loc.id.item_tree(db);
let item = &tree[loc.id.value]; let item = &tree[loc.id.value];
let enabled_params = enabled_params(&item.explicit_generic_params, &tree); let enabled_params =
enabled_params(&item.explicit_generic_params, &tree, loc.id.value.into());
let module = loc.container.module(db); let module = loc.container.module(db);
let func_data = db.function_data(id); let func_data = db.function_data(id);

View file

@ -57,11 +57,11 @@ use triomphe::Arc;
use crate::{ use crate::{
attr::Attrs, attr::Attrs,
db::DefDatabase, db::DefDatabase,
generics::{GenericParams, LifetimeParamData, TypeOrConstParamData}, generics::GenericParams,
path::{GenericArgs, ImportAlias, ModPath, Path, PathKind}, path::{GenericArgs, ImportAlias, ModPath, Path, PathKind},
type_ref::{Mutability, TraitRef, TypeBound, TypeRef}, type_ref::{Mutability, TraitRef, TypeBound, TypeRef},
visibility::{RawVisibility, VisibilityExplicitness}, visibility::{RawVisibility, VisibilityExplicitness},
BlockId, Lookup, BlockId, LocalLifetimeParamId, LocalTypeOrConstParamId, Lookup,
}; };
#[derive(Copy, Clone, Eq, PartialEq)] #[derive(Copy, Clone, Eq, PartialEq)]
@ -293,8 +293,8 @@ pub enum AttrOwner {
Variant(FileItemTreeId<Variant>), Variant(FileItemTreeId<Variant>),
Field(Idx<Field>), Field(Idx<Field>),
Param(Idx<Param>), Param(Idx<Param>),
TypeOrConstParamData(Idx<TypeOrConstParamData>), TypeOrConstParamData(GenericModItem, LocalTypeOrConstParamId),
LifetimeParamData(Idx<LifetimeParamData>), LifetimeParamData(GenericModItem, LocalLifetimeParamId),
} }
macro_rules! from_attrs { macro_rules! from_attrs {
@ -314,8 +314,6 @@ from_attrs!(
Variant(FileItemTreeId<Variant>), Variant(FileItemTreeId<Variant>),
Field(Idx<Field>), Field(Idx<Field>),
Param(Idx<Param>), Param(Idx<Param>),
TypeOrConstParamData(Idx<TypeOrConstParamData>),
LifetimeParamData(Idx<LifetimeParamData>),
); );
/// Trait implemented by all nodes in the item tree. /// Trait implemented by all nodes in the item tree.
@ -465,12 +463,49 @@ macro_rules! mod_items {
)+ )+
} }
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
pub enum GenericModItem {
$(
$(
#[cfg_attr(FALSE, $generic_params)]
$typ(FileItemTreeId<$typ>),
)?
)+
}
impl From<GenericModItem> for ModItem {
fn from(id: GenericModItem) -> ModItem {
match id {
$(
$(
#[cfg_attr(FALSE, $generic_params)]
GenericModItem::$typ(id) => ModItem::$typ(id),
)?
)+
}
}
}
impl From<GenericModItem> for AttrOwner {
fn from(t: GenericModItem) -> AttrOwner {
AttrOwner::ModItem(t.into())
}
}
$( $(
impl From<FileItemTreeId<$typ>> for ModItem { impl From<FileItemTreeId<$typ>> for ModItem {
fn from(id: FileItemTreeId<$typ>) -> ModItem { fn from(id: FileItemTreeId<$typ>) -> ModItem {
ModItem::$typ(id) ModItem::$typ(id)
} }
} }
$(
#[cfg_attr(FALSE, $generic_params)]
impl From<FileItemTreeId<$typ>> for GenericModItem {
fn from(id: FileItemTreeId<$typ>) -> GenericModItem {
GenericModItem::$typ(id)
}
}
)?
)+ )+
$( $(

View file

@ -4,6 +4,7 @@ use std::collections::hash_map::Entry;
use hir_expand::{mod_path::path, name, name::AsName, span_map::SpanMapRef, HirFileId}; use hir_expand::{mod_path::path, name, name::AsName, span_map::SpanMapRef, HirFileId};
use la_arena::Arena; use la_arena::Arena;
use rustc_hash::FxHashMap;
use span::{AstIdMap, SyntaxContextId}; use span::{AstIdMap, SyntaxContextId};
use syntax::{ use syntax::{
ast::{self, HasModuleItem, HasName, HasTypeBounds, IsString}, ast::{self, HasModuleItem, HasName, HasTypeBounds, IsString},
@ -16,11 +17,11 @@ use crate::{
generics::{GenericParams, GenericParamsCollector, TypeParamData, TypeParamProvenance}, generics::{GenericParams, GenericParamsCollector, TypeParamData, TypeParamProvenance},
item_tree::{ item_tree::{
AssocItem, AttrOwner, Const, Either, Enum, ExternBlock, ExternCrate, Field, FieldAstId, AssocItem, AttrOwner, Const, Either, Enum, ExternBlock, ExternCrate, Field, FieldAstId,
Fields, FileItemTreeId, FnFlags, Function, GenericArgs, Idx, IdxRange, Impl, ImportAlias, Fields, FileItemTreeId, FnFlags, Function, GenericArgs, GenericModItem, Idx, IdxRange,
Interned, ItemTree, ItemTreeData, ItemTreeNode, Macro2, MacroCall, MacroRules, Mod, Impl, ImportAlias, Interned, ItemTree, ItemTreeData, ItemTreeNode, Macro2, MacroCall,
ModItem, ModKind, ModPath, Mutability, Name, Param, ParamAstId, Path, Range, RawAttrs, MacroRules, Mod, ModItem, ModKind, ModPath, Mutability, Name, Param, ParamAstId, Path,
RawIdx, RawVisibilityId, Static, Struct, StructKind, Trait, TraitAlias, TypeAlias, Union, Range, RawAttrs, RawIdx, RawVisibilityId, Static, Struct, StructKind, Trait, TraitAlias,
Use, UseTree, UseTreeKind, Variant, TypeAlias, Union, Use, UseTree, UseTreeKind, Variant,
}, },
path::AssociatedTypeBinding, path::AssociatedTypeBinding,
type_ref::{LifetimeRef, TraitBoundModifier, TraitRef, TypeBound, TypeRef}, type_ref::{LifetimeRef, TraitBoundModifier, TraitRef, TypeBound, TypeRef},
@ -36,6 +37,8 @@ pub(super) struct Ctx<'a> {
db: &'a dyn DefDatabase, db: &'a dyn DefDatabase,
tree: ItemTree, tree: ItemTree,
source_ast_id_map: Arc<AstIdMap>, source_ast_id_map: Arc<AstIdMap>,
generic_param_attr_buffer:
FxHashMap<Either<LocalTypeOrConstParamId, LocalLifetimeParamId>, RawAttrs>,
body_ctx: crate::lower::LowerCtx<'a>, body_ctx: crate::lower::LowerCtx<'a>,
} }
@ -44,6 +47,7 @@ impl<'a> Ctx<'a> {
Self { Self {
db, db,
tree: ItemTree::default(), tree: ItemTree::default(),
generic_param_attr_buffer: FxHashMap::default(),
source_ast_id_map: db.ast_id_map(file), source_ast_id_map: db.ast_id_map(file),
body_ctx: crate::lower::LowerCtx::new(db, file), body_ctx: crate::lower::LowerCtx::new(db, file),
} }
@ -56,6 +60,7 @@ impl<'a> Ctx<'a> {
pub(super) fn lower_module_items(mut self, item_owner: &dyn HasModuleItem) -> ItemTree { pub(super) fn lower_module_items(mut self, item_owner: &dyn HasModuleItem) -> ItemTree {
self.tree.top_level = self.tree.top_level =
item_owner.items().flat_map(|item| self.lower_mod_item(&item)).collect(); item_owner.items().flat_map(|item| self.lower_mod_item(&item)).collect();
assert!(self.generic_param_attr_buffer.is_empty());
self.tree self.tree
} }
@ -89,6 +94,7 @@ impl<'a> Ctx<'a> {
} }
} }
assert!(self.generic_param_attr_buffer.is_empty());
self.tree self.tree
} }
@ -117,6 +123,7 @@ impl<'a> Ctx<'a> {
} }
} }
assert!(self.generic_param_attr_buffer.is_empty());
self.tree self.tree
} }
@ -185,10 +192,12 @@ impl<'a> Ctx<'a> {
let visibility = self.lower_visibility(strukt); let visibility = self.lower_visibility(strukt);
let name = strukt.name()?.as_name(); let name = strukt.name()?.as_name();
let ast_id = self.source_ast_id_map.ast_id(strukt); let ast_id = self.source_ast_id_map.ast_id(strukt);
let generic_params = self.lower_generic_params(HasImplicitSelf::No, strukt);
let fields = self.lower_fields(&strukt.kind()); let fields = self.lower_fields(&strukt.kind());
let generic_params = self.lower_generic_params(HasImplicitSelf::No, strukt);
let res = Struct { name, visibility, generic_params, fields, ast_id }; let res = Struct { name, visibility, generic_params, fields, ast_id };
Some(id(self.data().structs.alloc(res))) let id = id(self.data().structs.alloc(res));
self.write_generic_params_attributes(id.into());
Some(id)
} }
fn lower_fields(&mut self, strukt_kind: &ast::StructKind) -> Fields { fn lower_fields(&mut self, strukt_kind: &ast::StructKind) -> Fields {
@ -252,28 +261,32 @@ impl<'a> Ctx<'a> {
let visibility = self.lower_visibility(union); let visibility = self.lower_visibility(union);
let name = union.name()?.as_name(); let name = union.name()?.as_name();
let ast_id = self.source_ast_id_map.ast_id(union); let ast_id = self.source_ast_id_map.ast_id(union);
let generic_params = self.lower_generic_params(HasImplicitSelf::No, union);
let fields = match union.record_field_list() { let fields = match union.record_field_list() {
Some(record_field_list) => self.lower_fields(&StructKind::Record(record_field_list)), Some(record_field_list) => self.lower_fields(&StructKind::Record(record_field_list)),
None => Fields::Record(IdxRange::new(self.next_field_idx()..self.next_field_idx())), None => Fields::Record(IdxRange::new(self.next_field_idx()..self.next_field_idx())),
}; };
let generic_params = self.lower_generic_params(HasImplicitSelf::No, union);
let res = Union { name, visibility, generic_params, fields, ast_id }; let res = Union { name, visibility, generic_params, fields, ast_id };
Some(id(self.data().unions.alloc(res))) let id = id(self.data().unions.alloc(res));
self.write_generic_params_attributes(id.into());
Some(id)
} }
fn lower_enum(&mut self, enum_: &ast::Enum) -> Option<FileItemTreeId<Enum>> { fn lower_enum(&mut self, enum_: &ast::Enum) -> Option<FileItemTreeId<Enum>> {
let visibility = self.lower_visibility(enum_); let visibility = self.lower_visibility(enum_);
let name = enum_.name()?.as_name(); let name = enum_.name()?.as_name();
let ast_id = self.source_ast_id_map.ast_id(enum_); let ast_id = self.source_ast_id_map.ast_id(enum_);
let generic_params = self.lower_generic_params(HasImplicitSelf::No, enum_);
let variants = match &enum_.variant_list() { let variants = match &enum_.variant_list() {
Some(variant_list) => self.lower_variants(variant_list), Some(variant_list) => self.lower_variants(variant_list),
None => { None => {
FileItemTreeId(self.next_variant_idx())..FileItemTreeId(self.next_variant_idx()) FileItemTreeId(self.next_variant_idx())..FileItemTreeId(self.next_variant_idx())
} }
}; };
let generic_params = self.lower_generic_params(HasImplicitSelf::No, enum_);
let res = Enum { name, visibility, generic_params, variants, ast_id }; let res = Enum { name, visibility, generic_params, variants, ast_id };
Some(id(self.data().enums.alloc(res))) let id = id(self.data().enums.alloc(res));
self.write_generic_params_attributes(id.into());
Some(id)
} }
fn lower_variants(&mut self, variants: &ast::VariantList) -> Range<FileItemTreeId<Variant>> { fn lower_variants(&mut self, variants: &ast::VariantList) -> Range<FileItemTreeId<Variant>> {
@ -414,7 +427,9 @@ impl<'a> Ctx<'a> {
flags, flags,
}; };
Some(id(self.data().functions.alloc(res))) let id = id(self.data().functions.alloc(res));
self.write_generic_params_attributes(id.into());
Some(id)
} }
fn lower_type_alias( fn lower_type_alias(
@ -428,7 +443,9 @@ impl<'a> Ctx<'a> {
let ast_id = self.source_ast_id_map.ast_id(type_alias); let ast_id = self.source_ast_id_map.ast_id(type_alias);
let generic_params = self.lower_generic_params(HasImplicitSelf::No, type_alias); let generic_params = self.lower_generic_params(HasImplicitSelf::No, type_alias);
let res = TypeAlias { name, visibility, bounds, generic_params, type_ref, ast_id }; let res = TypeAlias { name, visibility, bounds, generic_params, type_ref, ast_id };
Some(id(self.data().type_aliases.alloc(res))) let id = id(self.data().type_aliases.alloc(res));
self.write_generic_params_attributes(id.into());
Some(id)
} }
fn lower_static(&mut self, static_: &ast::Static) -> Option<FileItemTreeId<Static>> { fn lower_static(&mut self, static_: &ast::Static) -> Option<FileItemTreeId<Static>> {
@ -475,8 +492,6 @@ impl<'a> Ctx<'a> {
let name = trait_def.name()?.as_name(); let name = trait_def.name()?.as_name();
let visibility = self.lower_visibility(trait_def); let visibility = self.lower_visibility(trait_def);
let ast_id = self.source_ast_id_map.ast_id(trait_def); let ast_id = self.source_ast_id_map.ast_id(trait_def);
let generic_params =
self.lower_generic_params(HasImplicitSelf::Yes(trait_def.type_bound_list()), trait_def);
let is_auto = trait_def.auto_token().is_some(); let is_auto = trait_def.auto_token().is_some();
let is_unsafe = trait_def.unsafe_token().is_some(); let is_unsafe = trait_def.unsafe_token().is_some();
@ -487,8 +502,12 @@ impl<'a> Ctx<'a> {
.filter_map(|item_node| self.lower_assoc_item(&item_node)) .filter_map(|item_node| self.lower_assoc_item(&item_node))
.collect(); .collect();
let generic_params =
self.lower_generic_params(HasImplicitSelf::Yes(trait_def.type_bound_list()), trait_def);
let def = Trait { name, visibility, generic_params, is_auto, is_unsafe, items, ast_id }; let def = Trait { name, visibility, generic_params, is_auto, is_unsafe, items, ast_id };
Some(id(self.data().traits.alloc(def))) let id = id(self.data().traits.alloc(def));
self.write_generic_params_attributes(id.into());
Some(id)
} }
fn lower_trait_alias( fn lower_trait_alias(
@ -504,19 +523,18 @@ impl<'a> Ctx<'a> {
); );
let alias = TraitAlias { name, visibility, generic_params, ast_id }; let alias = TraitAlias { name, visibility, generic_params, ast_id };
Some(id(self.data().trait_aliases.alloc(alias))) let id = id(self.data().trait_aliases.alloc(alias));
self.write_generic_params_attributes(id.into());
Some(id)
} }
fn lower_impl(&mut self, impl_def: &ast::Impl) -> Option<FileItemTreeId<Impl>> { fn lower_impl(&mut self, impl_def: &ast::Impl) -> Option<FileItemTreeId<Impl>> {
let ast_id = self.source_ast_id_map.ast_id(impl_def); let ast_id = self.source_ast_id_map.ast_id(impl_def);
// Note that trait impls don't get implicit `Self` unlike traits, because here they are a
// type alias rather than a type parameter, so this is handled by the resolver.
let generic_params = self.lower_generic_params(HasImplicitSelf::No, impl_def);
// FIXME: If trait lowering fails, due to a non PathType for example, we treat this impl // FIXME: If trait lowering fails, due to a non PathType for example, we treat this impl
// as if it was an non-trait impl. Ideally we want to create a unique missing ref that only // as if it was an non-trait impl. Ideally we want to create a unique missing ref that only
// equals itself. // equals itself.
let target_trait = impl_def.trait_().and_then(|tr| self.lower_trait_ref(&tr));
let self_ty = self.lower_type_ref(&impl_def.self_ty()?); let self_ty = self.lower_type_ref(&impl_def.self_ty()?);
let target_trait = impl_def.trait_().and_then(|tr| self.lower_trait_ref(&tr));
let is_negative = impl_def.excl_token().is_some(); let is_negative = impl_def.excl_token().is_some();
let is_unsafe = impl_def.unsafe_token().is_some(); let is_unsafe = impl_def.unsafe_token().is_some();
@ -527,9 +545,14 @@ impl<'a> Ctx<'a> {
.flat_map(|it| it.assoc_items()) .flat_map(|it| it.assoc_items())
.filter_map(|item| self.lower_assoc_item(&item)) .filter_map(|item| self.lower_assoc_item(&item))
.collect(); .collect();
// Note that trait impls don't get implicit `Self` unlike traits, because here they are a
// type alias rather than a type parameter, so this is handled by the resolver.
let generic_params = self.lower_generic_params(HasImplicitSelf::No, impl_def);
let res = let res =
Impl { generic_params, target_trait, self_ty, is_negative, is_unsafe, items, ast_id }; Impl { generic_params, target_trait, self_ty, is_negative, is_unsafe, items, ast_id };
Some(id(self.data().impls.alloc(res))) let id = id(self.data().impls.alloc(res));
self.write_generic_params_attributes(id.into());
Some(id)
} }
fn lower_use(&mut self, use_item: &ast::Use) -> Option<FileItemTreeId<Use>> { fn lower_use(&mut self, use_item: &ast::Use) -> Option<FileItemTreeId<Use>> {
@ -616,11 +639,30 @@ impl<'a> Ctx<'a> {
id(self.data().extern_blocks.alloc(res)) id(self.data().extern_blocks.alloc(res))
} }
fn write_generic_params_attributes(&mut self, parent: GenericModItem) {
self.generic_param_attr_buffer.drain().for_each(|(idx, attrs)| {
self.tree.attrs.insert(
match idx {
Either::Left(id) => AttrOwner::TypeOrConstParamData(parent, id),
Either::Right(id) => AttrOwner::LifetimeParamData(parent, id),
},
attrs,
);
})
}
fn lower_generic_params( fn lower_generic_params(
&mut self, &mut self,
has_implicit_self: HasImplicitSelf, has_implicit_self: HasImplicitSelf,
node: &dyn ast::HasGenericParams, node: &dyn ast::HasGenericParams,
) -> Interned<GenericParams> { ) -> Interned<GenericParams> {
debug_assert!(self.generic_param_attr_buffer.is_empty(),);
let add_param_attrs = |item: Either<LocalTypeOrConstParamId, LocalLifetimeParamId>,
param| {
let attrs = RawAttrs::new(self.db.upcast(), &param, self.body_ctx.span_map());
debug_assert!(self.generic_param_attr_buffer.insert(item, attrs).is_none());
};
let mut generics = GenericParamsCollector::default(); let mut generics = GenericParamsCollector::default();
if let HasImplicitSelf::Yes(bounds) = has_implicit_self { if let HasImplicitSelf::Yes(bounds) = has_implicit_self {
@ -635,28 +677,13 @@ impl<'a> Ctx<'a> {
); );
// add super traits as bounds on Self // add super traits as bounds on Self
// i.e., `trait Foo: Bar` is equivalent to `trait Foo where Self: Bar` // i.e., `trait Foo: Bar` is equivalent to `trait Foo where Self: Bar`
let self_param = TypeRef::Path(name![Self].into()); generics.fill_bounds(
generics.fill_bounds(&self.body_ctx, bounds, Either::Left(self_param)); &self.body_ctx,
bounds,
Either::Left(TypeRef::Path(name![Self].into())),
);
} }
let add_param_attrs = |item: Either<LocalTypeOrConstParamId, LocalLifetimeParamId>,
param| {
let attrs = RawAttrs::new(self.db.upcast(), &param, self.body_ctx.span_map());
// This is identical to the body of `Ctx::add_attrs()` but we can't call that here
// because it requires `&mut self` and the call to `generics.fill()` below also
// references `self`.
match self.tree.attrs.entry(match item {
Either::Right(id) => id.into(),
Either::Left(id) => id.into(),
}) {
Entry::Occupied(mut entry) => {
*entry.get_mut() = entry.get().merge(attrs);
}
Entry::Vacant(entry) => {
entry.insert(attrs);
}
}
};
generics.fill(&self.body_ctx, node, add_param_attrs); generics.fill(&self.body_ctx, node, add_param_attrs);
Interned::new(generics.finish()) Interned::new(generics.finish())

View file

@ -8,8 +8,8 @@ use crate::{
generics::{TypeOrConstParamData, WherePredicate, WherePredicateTypeTarget}, generics::{TypeOrConstParamData, WherePredicate, WherePredicateTypeTarget},
item_tree::{ item_tree::{
AttrOwner, Const, DefDatabase, Enum, ExternBlock, ExternCrate, Field, FieldAstId, Fields, AttrOwner, Const, DefDatabase, Enum, ExternBlock, ExternCrate, Field, FieldAstId, Fields,
FileItemTreeId, FnFlags, Function, GenericParams, Impl, Interned, ItemTree, Macro2, FileItemTreeId, FnFlags, Function, GenericModItem, GenericParams, Impl, Interned, ItemTree,
MacroCall, MacroRules, Mod, ModItem, ModKind, Param, ParamAstId, Path, RawAttrs, Macro2, MacroCall, MacroRules, Mod, ModItem, ModKind, Param, ParamAstId, Path, RawAttrs,
RawVisibilityId, Static, Struct, Trait, TraitAlias, TypeAlias, TypeBound, TypeRef, Union, RawVisibilityId, Static, Struct, Trait, TraitAlias, TypeAlias, TypeBound, TypeRef, Union,
Use, UseTree, UseTreeKind, Variant, Use, UseTree, UseTreeKind, Variant,
}, },
@ -276,7 +276,7 @@ impl Printer<'_> {
w!(self, "extern \"{}\" ", abi); w!(self, "extern \"{}\" ", abi);
} }
w!(self, "fn {}", name.display(self.db.upcast())); w!(self, "fn {}", name.display(self.db.upcast()));
self.print_generic_params(explicit_generic_params); self.print_generic_params(explicit_generic_params, it.into());
w!(self, "("); w!(self, "(");
if !params.is_empty() { if !params.is_empty() {
self.indented(|this| { self.indented(|this| {
@ -316,7 +316,7 @@ impl Printer<'_> {
self.print_ast_id(ast_id.erase()); self.print_ast_id(ast_id.erase());
self.print_visibility(*visibility); self.print_visibility(*visibility);
w!(self, "struct {}", name.display(self.db.upcast())); w!(self, "struct {}", name.display(self.db.upcast()));
self.print_generic_params(generic_params); self.print_generic_params(generic_params, it.into());
self.print_fields_and_where_clause(fields, generic_params); self.print_fields_and_where_clause(fields, generic_params);
if matches!(fields, Fields::Record(_)) { if matches!(fields, Fields::Record(_)) {
wln!(self); wln!(self);
@ -329,7 +329,7 @@ impl Printer<'_> {
self.print_ast_id(ast_id.erase()); self.print_ast_id(ast_id.erase());
self.print_visibility(*visibility); self.print_visibility(*visibility);
w!(self, "union {}", name.display(self.db.upcast())); w!(self, "union {}", name.display(self.db.upcast()));
self.print_generic_params(generic_params); self.print_generic_params(generic_params, it.into());
self.print_fields_and_where_clause(fields, generic_params); self.print_fields_and_where_clause(fields, generic_params);
if matches!(fields, Fields::Record(_)) { if matches!(fields, Fields::Record(_)) {
wln!(self); wln!(self);
@ -342,7 +342,7 @@ impl Printer<'_> {
self.print_ast_id(ast_id.erase()); self.print_ast_id(ast_id.erase());
self.print_visibility(*visibility); self.print_visibility(*visibility);
w!(self, "enum {}", name.display(self.db.upcast())); w!(self, "enum {}", name.display(self.db.upcast()));
self.print_generic_params(generic_params); self.print_generic_params(generic_params, it.into());
self.print_where_clause_and_opening_brace(generic_params); self.print_where_clause_and_opening_brace(generic_params);
self.indented(|this| { self.indented(|this| {
for variant in FileItemTreeId::range_iter(variants.clone()) { for variant in FileItemTreeId::range_iter(variants.clone()) {
@ -394,7 +394,7 @@ impl Printer<'_> {
w!(self, "auto "); w!(self, "auto ");
} }
w!(self, "trait {}", name.display(self.db.upcast())); w!(self, "trait {}", name.display(self.db.upcast()));
self.print_generic_params(generic_params); self.print_generic_params(generic_params, it.into());
self.print_where_clause_and_opening_brace(generic_params); self.print_where_clause_and_opening_brace(generic_params);
self.indented(|this| { self.indented(|this| {
for item in &**items { for item in &**items {
@ -408,7 +408,7 @@ impl Printer<'_> {
self.print_ast_id(ast_id.erase()); self.print_ast_id(ast_id.erase());
self.print_visibility(*visibility); self.print_visibility(*visibility);
w!(self, "trait {}", name.display(self.db.upcast())); w!(self, "trait {}", name.display(self.db.upcast()));
self.print_generic_params(generic_params); self.print_generic_params(generic_params, it.into());
w!(self, " = "); w!(self, " = ");
self.print_where_clause(generic_params); self.print_where_clause(generic_params);
w!(self, ";"); w!(self, ";");
@ -429,7 +429,7 @@ impl Printer<'_> {
w!(self, "unsafe"); w!(self, "unsafe");
} }
w!(self, "impl"); w!(self, "impl");
self.print_generic_params(generic_params); self.print_generic_params(generic_params, it.into());
w!(self, " "); w!(self, " ");
if *is_negative { if *is_negative {
w!(self, "!"); w!(self, "!");
@ -453,7 +453,7 @@ impl Printer<'_> {
self.print_ast_id(ast_id.erase()); self.print_ast_id(ast_id.erase());
self.print_visibility(*visibility); self.print_visibility(*visibility);
w!(self, "type {}", name.display(self.db.upcast())); w!(self, "type {}", name.display(self.db.upcast()));
self.print_generic_params(generic_params); self.print_generic_params(generic_params, it.into());
if !bounds.is_empty() { if !bounds.is_empty() {
w!(self, ": "); w!(self, ": ");
self.print_type_bounds(bounds); self.print_type_bounds(bounds);
@ -525,7 +525,7 @@ impl Printer<'_> {
print_path(self.db, path, self).unwrap(); print_path(self.db, path, self).unwrap();
} }
fn print_generic_params(&mut self, params: &GenericParams) { fn print_generic_params(&mut self, params: &GenericParams, parent: GenericModItem) {
if params.is_empty() { if params.is_empty() {
return; return;
} }
@ -537,7 +537,7 @@ impl Printer<'_> {
w!(self, ", "); w!(self, ", ");
} }
first = false; first = false;
self.print_attrs_of(idx, " "); self.print_attrs_of(AttrOwner::LifetimeParamData(parent, idx), " ");
w!(self, "{}", lt.name.display(self.db.upcast())); w!(self, "{}", lt.name.display(self.db.upcast()));
} }
for (idx, x) in params.type_or_consts.iter() { for (idx, x) in params.type_or_consts.iter() {
@ -545,7 +545,7 @@ impl Printer<'_> {
w!(self, ", "); w!(self, ", ");
} }
first = false; first = false;
self.print_attrs_of(idx, " "); self.print_attrs_of(AttrOwner::TypeOrConstParamData(parent, idx), " ");
match x { match x {
TypeOrConstParamData::TypeParamData(ty) => match &ty.name { TypeOrConstParamData::TypeParamData(ty) => match &ty.name {
Some(name) => w!(self, "{}", name.display(self.db.upcast())), Some(name) => w!(self, "{}", name.display(self.db.upcast())),

View file

@ -427,10 +427,18 @@ fn generics_with_attributes() {
check( check(
r#" r#"
struct S<#[cfg(never)] T>; struct S<#[cfg(never)] T>;
struct S<A, B, #[cfg(never)] C>;
struct S<A, #[cfg(never)] B, C>;
"#, "#,
expect![[r#" expect![[r#"
// AstId: 1 // AstId: 1
pub(self) struct S<#[cfg(never)] T>; pub(self) struct S<#[cfg(never)] T>;
// AstId: 2
pub(self) struct S<A, B, #[cfg(never)] C>;
// AstId: 3
pub(self) struct S<A, #[cfg(never)] B, C>;
"#]], "#]],
) )
} }

View file

@ -5,7 +5,7 @@
use std::{cmp::Ordering, iter, mem, ops::Not}; use std::{cmp::Ordering, iter, mem, ops::Not};
use base_db::{CrateId, Dependency, FileId}; use base_db::{CrateId, CrateOrigin, Dependency, FileId, LangCrateOrigin};
use cfg::{CfgExpr, CfgOptions}; use cfg::{CfgExpr, CfgOptions};
use either::Either; use either::Either;
use hir_expand::{ use hir_expand::{
@ -279,7 +279,8 @@ impl DefCollector<'_> {
fn seed_with_top_level(&mut self) { fn seed_with_top_level(&mut self) {
let _p = tracing::span!(tracing::Level::INFO, "seed_with_top_level").entered(); let _p = tracing::span!(tracing::Level::INFO, "seed_with_top_level").entered();
let file_id = self.db.crate_graph()[self.def_map.krate].root_file_id; let crate_graph = self.db.crate_graph();
let file_id = crate_graph[self.def_map.krate].root_file_id;
let item_tree = self.db.file_item_tree(file_id.into()); let item_tree = self.db.file_item_tree(file_id.into());
let attrs = item_tree.top_level_attrs(self.db, self.def_map.krate); let attrs = item_tree.top_level_attrs(self.db, self.def_map.krate);
let crate_data = Arc::get_mut(&mut self.def_map.data).unwrap(); let crate_data = Arc::get_mut(&mut self.def_map.data).unwrap();
@ -318,8 +319,43 @@ impl DefCollector<'_> {
self.is_proc_macro = true; self.is_proc_macro = true;
} }
} }
() if *attr_name == hir_expand::name![no_core] => crate_data.no_core = true, () if *attr_name == hir_expand::name![no_core] => {
() if *attr_name == hir_expand::name![no_std] => crate_data.no_std = true, if let Some((core, _)) =
crate_data.extern_prelude.iter().find(|(_, (root, _))| {
matches!(
crate_graph[root.krate].origin,
CrateOrigin::Lang(LangCrateOrigin::Core)
)
})
{
crate_data.extern_prelude.remove(&core.clone());
}
crate_data.no_core = true
}
() if *attr_name == hir_expand::name![no_std] => {
if let Some((alloc, _)) =
crate_data.extern_prelude.iter().find(|(_, (root, _))| {
matches!(
crate_graph[root.krate].origin,
CrateOrigin::Lang(LangCrateOrigin::Alloc)
)
})
{
crate_data.extern_prelude.remove(&alloc.clone());
}
if let Some((std, _)) =
crate_data.extern_prelude.iter().find(|(_, (root, _))| {
matches!(
crate_graph[root.krate].origin,
CrateOrigin::Lang(LangCrateOrigin::Std)
)
})
{
crate_data.extern_prelude.remove(&std.clone());
}
crate_data.no_std = true
}
() if attr_name.as_text().as_deref() == Some("rustc_coherence_is_core") => { () if attr_name.as_text().as_deref() == Some("rustc_coherence_is_core") => {
crate_data.rustc_coherence_is_core = true; crate_data.rustc_coherence_is_core = true;
} }

View file

@ -1056,23 +1056,7 @@ impl<'a> TyLoweringContext<'a> {
let clause = match bound.as_ref() { let clause = match bound.as_ref() {
TypeBound::Path(path, TraitBoundModifier::None) => { TypeBound::Path(path, TraitBoundModifier::None) => {
trait_ref = self.lower_trait_ref_from_path(path, Some(self_ty)); trait_ref = self.lower_trait_ref_from_path(path, Some(self_ty));
trait_ref trait_ref.clone().map(WhereClause::Implemented).map(crate::wrap_empty_binders)
.clone()
.filter(|tr| {
// ignore `T: Drop` or `T: Destruct` bounds.
// - `T: ~const Drop` has a special meaning in Rust 1.61 that we don't implement.
// (So ideally, we'd only ignore `~const Drop` here)
// - `Destruct` impls are built-in in 1.62 (current nightly as of 08-04-2022), so until
// the builtin impls are supported by Chalk, we ignore them here.
if let Some(lang) = self.db.lang_attr(tr.hir_trait_id().into()) {
if matches!(lang, LangItem::Drop | LangItem::Destruct) {
return false;
}
}
true
})
.map(WhereClause::Implemented)
.map(crate::wrap_empty_binders)
} }
TypeBound::Path(path, TraitBoundModifier::Maybe) => { TypeBound::Path(path, TraitBoundModifier::Maybe) => {
let sized_trait = self let sized_trait = self

View file

@ -1599,85 +1599,6 @@ fn f(s: S) {
); );
} }
#[test]
fn rust_161_option_clone() {
check_types(
r#"
//- minicore: option, drop
fn test(o: &Option<i32>) {
o.my_clone();
//^^^^^^^^^^^^ Option<i32>
}
pub trait MyClone: Sized {
fn my_clone(&self) -> Self;
}
impl<T> const MyClone for Option<T>
where
T: ~const MyClone + ~const Drop + ~const Destruct,
{
fn my_clone(&self) -> Self {
match self {
Some(x) => Some(x.my_clone()),
None => None,
}
}
}
impl const MyClone for i32 {
fn my_clone(&self) -> Self {
*self
}
}
pub trait Destruct {}
impl<T: ?Sized> const Destruct for T {}
"#,
);
}
#[test]
fn rust_162_option_clone() {
check_types(
r#"
//- minicore: option, drop
fn test(o: &Option<i32>) {
o.my_clone();
//^^^^^^^^^^^^ Option<i32>
}
pub trait MyClone: Sized {
fn my_clone(&self) -> Self;
}
impl<T> const MyClone for Option<T>
where
T: ~const MyClone + ~const Destruct,
{
fn my_clone(&self) -> Self {
match self {
Some(x) => Some(x.my_clone()),
None => None,
}
}
}
impl const MyClone for i32 {
fn my_clone(&self) -> Self {
*self
}
}
#[lang = "destruct"]
pub trait Destruct {}
"#,
);
}
#[test] #[test]
fn tuple_struct_pattern_with_unmatched_args_crash() { fn tuple_struct_pattern_with_unmatched_args_crash() {
check_infer( check_infer(
@ -2040,3 +1961,17 @@ fn main() {
"#, "#,
) )
} }
#[test]
fn cfg_first_trait_param_16141() {
check_no_mismatches(
r#"
//- minicore: sized, coerce_unsized
trait Bar {
fn bar(&self) {}
}
impl<#[cfg(feature = "a-feature")] A> Bar for (){}
"#,
)
}

View file

@ -238,6 +238,7 @@ fn infer_for_loop() {
//- minicore: iterator //- minicore: iterator
//- /main.rs crate:main deps:alloc //- /main.rs crate:main deps:alloc
#![no_std] #![no_std]
extern crate alloc;
use alloc::collections::Vec; use alloc::collections::Vec;
fn test() { fn test() {