Skip to content

Commit

Permalink
Merge pull request #1051 from sbillig/enum-tuple-ctor-fix
Browse files Browse the repository at this point in the history
Fix cross-ingot inherent method resolution
  • Loading branch information
micahscopes authored Jan 28, 2025
2 parents 60eabe4 + e33b419 commit 5be9878
Show file tree
Hide file tree
Showing 16 changed files with 193 additions and 75 deletions.
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 4 additions & 0 deletions crates/driver/src/db.rs
Original file line number Diff line number Diff line change
Expand Up @@ -244,6 +244,10 @@ impl DriverDataBase {

pub struct DiagnosticsCollection<'db>(Vec<Box<dyn DiagnosticVoucher<'db> + 'db>>);
impl<'db> DiagnosticsCollection<'db> {
pub fn is_empty(&self) -> bool {
self.0.is_empty()
}

pub fn emit(&self, db: &'db DriverDataBase) {
let writer = BufferWriter::stderr(ColorChoice::Auto);
let mut buffer = writer.buffer();
Expand Down
1 change: 1 addition & 0 deletions crates/hir-analysis/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ test-utils.workspace = true
hir.workspace = true

[dev-dependencies]
camino.workspace = true
codespan-reporting.workspace = true
dir-test.workspace = true
# TODO move cs diagnostics utils
Expand Down
66 changes: 58 additions & 8 deletions crates/hir-analysis/src/name_resolution/path_resolver.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,9 @@ use super::{
use crate::{
name_resolution::{NameResKind, QueryDirective},
ty::{
adt_def::{lower_adt, AdtRef, AdtRefId},
func_def::lower_func,
adt_def::{lower_adt, AdtRefId},
binder::Binder,
func_def::{lower_func, FuncDef, HirFuncDefKind},
trait_def::TraitDef,
trait_lower::lower_trait,
ty_def::{InvalidCause, TyId},
Expand Down Expand Up @@ -249,6 +250,10 @@ pub struct ResolvedVariant<'db> {
}

impl<'db> ResolvedVariant<'db> {
pub fn new(ty: TyId<'db>, idx: usize, path: PathId<'db>) -> Self {
Self { ty, idx, path }
}

pub fn variant_def(&self, db: &'db dyn HirAnalysisDb) -> &'db VariantDef<'db> {
&self.enum_(db).variants(db.as_hir_db()).data(db.as_hir_db())[self.idx]
}
Expand All @@ -258,14 +263,59 @@ impl<'db> ResolvedVariant<'db> {
}

pub fn enum_(&self, db: &'db dyn HirAnalysisDb) -> Enum<'db> {
let AdtRef::Enum(enum_) = self.ty.adt_ref(db).unwrap().data(db) else {
unreachable!()
};
enum_
self.ty.as_enum(db).unwrap()
}

pub fn new(ty: TyId<'db>, idx: usize, path: PathId<'db>) -> Self {
Self { ty, idx, path }
pub fn iter_field_types(
&self,
db: &'db dyn HirAnalysisDb,
) -> impl Iterator<Item = Binder<TyId<'db>>> {
self.ty
.adt_def(db)
.unwrap()
.fields(db)
.get(self.idx)
.unwrap()
.iter_types(db)
}

pub fn constructor_func_ty(&self, db: &'db dyn HirAnalysisDb) -> Option<TyId<'db>> {
let mut ty = TyId::func(db, self.to_funcdef(db)?);

for &arg in self.ty.generic_args(db) {
if ty.applicable_ty(db).is_some() {
ty = TyId::app(db, ty, arg);
}
}
Some(ty)
}

pub fn to_funcdef(&self, db: &'db dyn HirAnalysisDb) -> Option<FuncDef<'db>> {
if !matches!(self.variant_kind(db), VariantKind::Tuple(_)) {
return None;
}

let adt = self.ty.adt_def(db).unwrap();
let arg_tys = adt
.fields(db)
.get(self.idx)
.unwrap()
.iter_types(db)
.collect();

let adt_param_set = adt.param_set(db);

let mut ret_ty = TyId::adt(db, adt);
ret_ty = TyId::foldl(db, ret_ty, adt.param_set(db).params(db));

Some(FuncDef::new(
db,
HirFuncDefKind::VariantCtor(self.enum_(db), self.idx),
*self.variant_def(db).name.unwrap(),
*adt_param_set,
arg_tys,
Binder::bind(ret_ty),
))
}
}

Expand Down
55 changes: 3 additions & 52 deletions crates/hir-analysis/src/ty/method_table.rs
Original file line number Diff line number Diff line change
@@ -1,19 +1,15 @@
use hir::hir_def::{Enum, IdentId, Impl, IngotId, VariantKind};
use hir::hir_def::{IdentId, Impl, IngotId};
use rustc_hash::FxHashMap;

use super::{
adt_def::{lower_adt, AdtRefId},
binder::Binder,
canonical::Canonical,
func_def::{lower_func, FuncDef, HirFuncDefKind},
func_def::{lower_func, FuncDef},
ty_def::{InvalidCause, TyBase, TyId},
ty_lower::lower_hir_ty,
unify::UnificationTable,
};
use crate::{
ty::{ty_def::TyData, ty_lower::GenericParamTypeSet},
HirAnalysisDb,
};
use crate::{ty::ty_def::TyData, HirAnalysisDb};

#[salsa::tracked(return_ref)]
pub(crate) fn collect_methods<'db>(
Expand All @@ -22,11 +18,7 @@ pub(crate) fn collect_methods<'db>(
) -> MethodTable<'db> {
let mut collector = MethodCollector::new(db, ingot);

let enums = ingot.all_enums(db.as_hir_db());
collector.collect_variant_ctors(enums);

let impls = ingot.all_impls(db.as_hir_db());

collector.collect_impls(impls);
collector.finalize()
}
Expand Down Expand Up @@ -150,47 +142,6 @@ impl<'db> MethodCollector<'db> {
}
}

fn collect_variant_ctors(&mut self, enums: &[Enum<'db>]) {
let hir_db = self.db.as_hir_db();
for &enum_ in enums {
let adt_ref = AdtRefId::new(self.db, enum_.into());
let adt = lower_adt(self.db, adt_ref);
for (i, variant) in enum_.variants(hir_db).data(hir_db).iter().enumerate() {
if !matches!(variant.kind, VariantKind::Tuple(_)) {
continue;
};
let (Some(name), Some(variant)) =
(variant.name.to_opt(), adt.fields(self.db).get(i))
else {
continue;
};

let arg_tys = variant.iter_types(self.db).collect();
let mut ret_ty = TyId::adt(self.db, adt);
let adt_param_set = adt.param_set(self.db);
ret_ty = TyId::foldl(self.db, ret_ty, adt.param_set(self.db).params(self.db));

let param_set = GenericParamTypeSet::new(
self.db,
adt_param_set.params_precursor(self.db).to_vec(),
adt_param_set.scope(self.db),
adt_param_set.len(self.db),
);

let func = FuncDef::new(
self.db,
HirFuncDefKind::VariantCtor(enum_, i),
name,
param_set,
arg_tys,
Binder::bind(ret_ty),
);

self.insert(ret_ty, func)
}
}
}

fn collect_impls(&mut self, impls: &[Impl<'db>]) {
for impl_ in impls {
let ty = match impl_.ty(self.db.as_hir_db()).to_opt() {
Expand Down
7 changes: 4 additions & 3 deletions crates/hir-analysis/src/ty/ty_check/expr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -447,9 +447,10 @@ impl<'db> TyChecker<'db> {
PathRes::EnumVariant(variant) => {
let ty = match variant.variant_kind(self.db) {
VariantKind::Unit => variant.ty,
VariantKind::Tuple(_) => self
.select_method_candidate_for_path(variant.ty, *path, span.path())
.unwrap_or_else(|| TyId::invalid(self.db, InvalidCause::Other)),
VariantKind::Tuple(_) => {
let ty = variant.constructor_func_ty(self.db).unwrap();
self.table.instantiate_to_term(ty)
}
VariantKind::Record(_) => {
let diag = BodyDiag::unit_variant_expected(
self.db,
Expand Down
6 changes: 5 additions & 1 deletion crates/hir-analysis/src/ty/ty_check/method_selection.rs
Original file line number Diff line number Diff line change
Expand Up @@ -156,7 +156,11 @@ impl<'db> CandidateAssembler<'db> {
}

fn assemble_inherent_method_candidates(&mut self) {
let ingot = self.scope.ingot(self.db.as_hir_db());
let ingot = self
.receiver_ty
.value
.ingot(self.db)
.unwrap_or_else(|| self.scope.ingot(self.db.as_hir_db()));
for &method in probe_method(self.db, ingot, self.receiver_ty, self.method_name) {
self.candidates.insert_inherent_method(method);
}
Expand Down
4 changes: 0 additions & 4 deletions crates/hir-analysis/src/ty/ty_lower.rs
Original file line number Diff line number Diff line change
Expand Up @@ -285,10 +285,6 @@ impl<'db> GenericParamTypeSet<'db> {
Self::new(db, Vec::new(), scope, 0)
}

pub(crate) fn len(self, db: &dyn HirAnalysisDb) -> usize {
self.params_precursor(db).len()
}

pub(crate) fn trait_self(&self, db: &'db dyn HirAnalysisDb) -> Option<TyId<'db>> {
let params = self.params_precursor(db);
let cand = params.first()?;
Expand Down
12 changes: 12 additions & 0 deletions crates/hir-analysis/test_files/corelib/method_resolution.fe
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
use core::{Option, panic}

fn f() -> usize {
let x = Option::Some(10)
let y = Option::default()

if y.is_some() {
y.unwrap()
} else {
x.unwrap_or_default()
}
}
35 changes: 35 additions & 0 deletions crates/hir-analysis/tests/corelib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
mod test_db;

use camino::Utf8Path;
use dir_test::{dir_test, Fixture};
use driver::DriverDataBase;

#[test]
fn analyze_corelib() {
let mut db = DriverDataBase::default();
let (core, _) = db.static_core_ingot();

let core_diags = db.run_on_ingot(core);
if !(core_diags.is_empty()) {
core_diags.emit(&db);
panic!("expected no diagnostics");
}
}

#[dir_test(
dir: "$CARGO_MANIFEST_DIR/test_files/corelib",
glob: "*.fe"
)]
fn corelib_standalone(fixture: Fixture<&str>) {
let mut db = DriverDataBase::default();
let path = Utf8Path::new(fixture.path());
let file_name = path.file_name().unwrap();
let (core, _) = db.static_core_ingot();
let (ingot, _) = db.standalone(file_name.into(), fixture.content(), core);

let local_diags = db.run_on_ingot(ingot);
if !local_diags.is_empty() {
local_diags.emit(&db);
panic!("expected no diagnostics");
}
}
2 changes: 1 addition & 1 deletion crates/hir-analysis/tests/test_db.rs
Original file line number Diff line number Diff line change
Expand Up @@ -182,7 +182,7 @@ impl<'db> HirPropertyFormatter<'db> {
(span, diag)
}

fn register_top_mod(&mut self, path: &str, text: &str, top_mod: TopLevelMod<'db>) {
pub fn register_top_mod(&mut self, path: &str, text: &str, top_mod: TopLevelMod<'db>) {
let file_id = self.code_span_files.add(path.to_string(), text.to_string());
self.top_mod_to_file.insert(top_mod, file_id);
}
Expand Down
6 changes: 4 additions & 2 deletions crates/uitest/tests/name_resolution.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,8 @@ fn run_name_resolution(fixture: Fixture<&str>) {
let mut db = DriverDataBase::default();
let path = Utf8Path::new(fixture.path());

let (ingot, file) = db.standalone_no_core(path, fixture.content());
let (core, _) = db.static_core_ingot();
let (ingot, file) = db.standalone(path, fixture.content(), core);
let top_mod = db.top_mod(ingot, file);

let diags = db.run_on_top_mod(top_mod);
Expand All @@ -37,7 +38,8 @@ mod wasm {
let mut db = DriverDataBase::default();
let path = Utf8Path::new(fixture.path());

let (ingot, file) = db.standalone_no_core(path, fixture.content());
let (core, _) = db.static_core_ingot();
let (ingot, file) = db.standalone(path, fixture.content(), core);
let top_mod = db.top_mod(ingot, file);
db.run_on_top_mod(top_mod);
}
Expand Down
2 changes: 0 additions & 2 deletions library/core/src/bar.fe

This file was deleted.

9 changes: 9 additions & 0 deletions library/core/src/default.fe
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
pub trait Default {
fn default() -> Self
}

impl Default for usize {
fn default() -> Self {
0
}
}
9 changes: 7 additions & 2 deletions library/core/src/lib.fe
Original file line number Diff line number Diff line change
@@ -1,2 +1,7 @@
/// remove once useful code is added to core
pub fn foo() { }
pub use option::Option
pub use default::Default

extern {
pub fn panic() -> !
pub fn todo() -> !
}
49 changes: 49 additions & 0 deletions library/core/src/option.fe
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
use ingot::Default
use ingot::panic

pub enum Option<T> {
Some(T),
None
}

impl<T> Option<T> {
pub fn is_some(self) -> bool {
match self {
Self::Some(_) => true
None => false
}
}

pub fn is_none(self) -> bool {
!self.is_some()
}

pub fn unwrap(self) -> T {
match self {
Self::Some(t) => t
None => panic()
}
}

pub fn unwrap_or(self, default: T) -> T {
match self {
Self::Some(x) => x
Self::None => default
}
}
}

impl<T> Option<T> where T: Default {
pub fn unwrap_or_default(self) -> T {
match self {
Self::Some(x) => x
Self::None => T::default()
}
}
}

impl<T> Default for Option<T> {
fn default() -> Self {
Self::None
}
}

0 comments on commit 5be9878

Please sign in to comment.