Skip to content

Commit 8eb8cd7

Browse files
committed
chore: union method type
1 parent 8c65bff commit 8eb8cd7

File tree

5 files changed

+188
-20
lines changed

5 files changed

+188
-20
lines changed

crates/els/completion.rs

Lines changed: 74 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ use erg_compiler::erg_parser::token::TokenKind;
2323
use erg_compiler::hir::Expr;
2424
use erg_compiler::module::SharedCompilerResource;
2525
use erg_compiler::ty::{HasType, ParamTy, Type};
26-
use erg_compiler::varinfo::{AbsLocation, VarInfo};
26+
use erg_compiler::varinfo::{AbsLocation, Mutability, VarInfo, VarKind};
2727
use TokenKind::*;
2828

2929
use lsp_types::{
@@ -35,17 +35,40 @@ use crate::_log;
3535
use crate::server::{ELSResult, Flags, RedirectableStdout, Server};
3636
use crate::util::{self, loc_to_pos, NormalizedUrl};
3737

38-
fn comp_item_kind(vi: &VarInfo) -> CompletionItemKind {
39-
match &vi.t {
38+
fn comp_item_kind(t: &Type, muty: Mutability) -> CompletionItemKind {
39+
match t {
4040
Type::Subr(subr) if subr.self_t().is_some() => CompletionItemKind::METHOD,
4141
Type::Quantified(quant) if quant.self_t().is_some() => CompletionItemKind::METHOD,
4242
Type::Subr(_) | Type::Quantified(_) => CompletionItemKind::FUNCTION,
4343
Type::ClassType => CompletionItemKind::CLASS,
4444
Type::TraitType => CompletionItemKind::INTERFACE,
45+
Type::Or(l, r) => {
46+
let l = comp_item_kind(l, muty);
47+
let r = comp_item_kind(r, muty);
48+
if l == r {
49+
l
50+
} else if muty.is_const() {
51+
CompletionItemKind::CONSTANT
52+
} else {
53+
CompletionItemKind::VARIABLE
54+
}
55+
}
56+
Type::And(l, r) => {
57+
let l = comp_item_kind(l, muty);
58+
let r = comp_item_kind(r, muty);
59+
if l == CompletionItemKind::VARIABLE {
60+
r
61+
} else {
62+
l
63+
}
64+
}
65+
Type::Refinement(r) => comp_item_kind(&r.t, muty),
66+
Type::Bounded { sub, .. } => comp_item_kind(sub, muty),
4567
t if matches!(&t.qual_name()[..], "Module" | "PyModule" | "GenericModule") => {
4668
CompletionItemKind::MODULE
4769
}
48-
_ if vi.muty.is_const() => CompletionItemKind::CONSTANT,
70+
Type::Type => CompletionItemKind::CONSTANT,
71+
_ if muty.is_const() => CompletionItemKind::CONSTANT,
4972
_ => CompletionItemKind::VARIABLE,
5073
}
5174
}
@@ -112,21 +135,24 @@ impl CompletionOrder {
112135
}
113136

114137
pub struct CompletionOrderSetter<'b> {
115-
vi: &'b VarInfo,
138+
t: &'b Type,
139+
kind: &'b VarKind,
116140
arg_pt: Option<&'b ParamTy>,
117141
mod_ctx: &'b Context, // for subtype judgement, not for variable lookup
118142
label: String,
119143
}
120144

121145
impl<'b> CompletionOrderSetter<'b> {
122146
pub fn new(
123-
vi: &'b VarInfo,
147+
t: &'b Type,
148+
kind: &'b VarKind,
124149
arg_pt: Option<&'b ParamTy>,
125150
mod_ctx: &'b Context,
126151
label: String,
127152
) -> Self {
128153
Self {
129-
vi,
154+
t,
155+
kind,
130156
arg_pt,
131157
mod_ctx,
132158
label,
@@ -140,7 +166,7 @@ impl<'b> CompletionOrderSetter<'b> {
140166
} else if self.label.starts_with('_') {
141167
orders.push(CompletionOrder::Escaped);
142168
}
143-
if self.vi.kind.is_builtin() {
169+
if self.kind.is_builtin() {
144170
orders.push(CompletionOrder::Builtin);
145171
}
146172
if self
@@ -152,11 +178,11 @@ impl<'b> CompletionOrderSetter<'b> {
152178
#[allow(clippy::blocks_in_conditions)]
153179
if self
154180
.arg_pt
155-
.map_or(false, |pt| self.mod_ctx.subtype_of(&self.vi.t, pt.typ()))
181+
.map_or(false, |pt| self.mod_ctx.subtype_of(self.t, pt.typ()))
156182
{
157183
orders.push(CompletionOrder::TypeMatched);
158184
} else if self.arg_pt.map_or(false, |pt| {
159-
let Some(return_t) = self.vi.t.return_t() else {
185+
let Some(return_t) = self.t.return_t() else {
160186
return false;
161187
};
162188
if return_t.has_qvar() {
@@ -196,7 +222,7 @@ fn external_item(name: &str, vi: &VarInfo, mod_name: &str) -> CompletionItem {
196222
let mut item =
197223
CompletionItem::new_simple(format!("{name} (import from {mod_name})"), vi.t.to_string());
198224
item.sort_text = Some(format!("{}_{}", CompletionOrder::STD_ITEM, item.label));
199-
item.kind = Some(comp_item_kind(vi));
225+
item.kind = Some(comp_item_kind(&vi.t, vi.muty));
200226
let import = if PYTHON_MODE {
201227
format!("from {mod_name} import {name}\n")
202228
} else {
@@ -467,10 +493,16 @@ impl<Checker: BuildRunnable, Parser: Parsable> Server<Checker, Parser> {
467493
continue;
468494
}
469495
let mut item = CompletionItem::new_simple(label, vi.t.to_string());
470-
CompletionOrderSetter::new(vi, arg_pt.as_ref(), mod_ctx, item.label.clone())
471-
.set(&mut item);
496+
CompletionOrderSetter::new(
497+
&vi.t,
498+
&vi.kind,
499+
arg_pt.as_ref(),
500+
mod_ctx,
501+
item.label.clone(),
502+
)
503+
.set(&mut item);
472504
// item.sort_text = Some(format!("{}_{}", CompletionOrder::OtherNamespace, item.label));
473-
item.kind = Some(comp_item_kind(vi));
505+
item.kind = Some(comp_item_kind(&vi.t, vi.muty));
474506
item.data = Some(Value::String(vi.def_loc.to_string()));
475507
let import = if PYTHON_MODE {
476508
format!("from {path} import {name}\n")
@@ -590,6 +622,25 @@ impl<Checker: BuildRunnable, Parser: Parsable> Server<Checker, Parser> {
590622
_log!(self, "module context not found: {uri}");
591623
return Ok(Some(CompletionResponse::Array(result)));
592624
};
625+
if PYTHON_MODE {
626+
if let Some(receiver_t) = &receiver_t {
627+
for (field, ty) in mod_ctx.context.fields(receiver_t) {
628+
let mut item =
629+
CompletionItem::new_simple(field.symbol.to_string(), ty.to_string());
630+
CompletionOrderSetter::new(
631+
&ty,
632+
&VarKind::Builtin,
633+
arg_pt.as_ref(),
634+
&mod_ctx.context,
635+
item.label.clone(),
636+
)
637+
.set(&mut item);
638+
item.kind = Some(comp_item_kind(&ty, Mutability::Immutable));
639+
already_appeared.insert(item.label.clone());
640+
result.push(item);
641+
}
642+
}
643+
}
593644
for (name, vi) in contexts.into_iter().flat_map(|ctx| ctx.local_dir()) {
594645
if comp_kind.should_be_method() && vi.vis.is_private() {
595646
continue;
@@ -621,9 +672,15 @@ impl<Checker: BuildRunnable, Parser: Parsable> Server<Checker, Parser> {
621672
}
622673
let readable_t = mod_ctx.context.readable_type(vi.t.clone());
623674
let mut item = CompletionItem::new_simple(label, readable_t.to_string());
624-
CompletionOrderSetter::new(vi, arg_pt.as_ref(), &mod_ctx.context, item.label.clone())
625-
.set(&mut item);
626-
item.kind = Some(comp_item_kind(vi));
675+
CompletionOrderSetter::new(
676+
&vi.t,
677+
&vi.kind,
678+
arg_pt.as_ref(),
679+
&mod_ctx.context,
680+
item.label.clone(),
681+
)
682+
.set(&mut item);
683+
item.kind = Some(comp_item_kind(&vi.t, vi.muty));
627684
item.data = Some(Value::String(vi.def_loc.to_string()));
628685
already_appeared.insert(item.label.clone());
629686
result.push(item);

crates/erg_compiler/context/compare.rs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -215,7 +215,11 @@ impl Context {
215215
Some((Type::Never, Type::Obj)) => (Absolutely, true),
216216
_ => (Maybe, false),
217217
},
218-
(Mono(n), Subr(_) | Quantified(_)) if &n[..] == "Subroutine" => (Absolutely, true),
218+
(Mono(n), Subr(_) | Quantified(_))
219+
if &n[..] == "Subroutine" || &n[..] == "GenericCallable" =>
220+
{
221+
(Absolutely, true)
222+
}
219223
(lhs, rhs) if lhs.is_mono_value_class() && rhs.is_mono_value_class() => {
220224
(Absolutely, false)
221225
}

crates/erg_compiler/context/initialize/classes.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2673,6 +2673,9 @@ impl Context {
26732673
Visibility::BUILTIN_PUBLIC,
26742674
);
26752675
bytes.register_trait_methods(mono(BYTES), bytes_seq);
2676+
bytes
2677+
.register_trait(self, poly(SEQUENCE, vec![ty_tp(Int)]))
2678+
.unwrap();
26762679
let mut bytes_eq = Self::builtin_methods(Some(mono(EQ)), 2);
26772680
bytes_eq.register_builtin_erg_impl(
26782681
OP_EQ,
@@ -3581,6 +3584,9 @@ impl Context {
35813584
Visibility::BUILTIN_PUBLIC,
35823585
);
35833586
bytearray_mut.register_trait_methods(mono(MUT_BYTEARRAY), bytearray_seq);
3587+
bytearray_mut
3588+
.register_trait(self, poly(SEQUENCE, vec![ty_tp(Int)]))
3589+
.unwrap();
35843590
let t_append = pr_met(
35853591
ref_mut(bytearray_mut_t.clone(), None),
35863592
vec![kw(KW_ELEM, int_interval(IntervalOp::Closed, 0, 255))],

crates/erg_compiler/context/inquire.rs

Lines changed: 82 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1379,11 +1379,27 @@ impl Context {
13791379
}
13801380
return Ok(method.method_info.clone());
13811381
}
1382-
Triple::Err(err) => {
1382+
Triple::Err(err) if ERG_MODE => {
13831383
return Err(err);
13841384
}
13851385
_ => {}
13861386
}
1387+
if PYTHON_MODE {
1388+
if let Some(subr_t) = self.get_union_attr_type_by_name(attr_name) {
1389+
let muty = Mutability::from(&attr_name.inspect()[..]);
1390+
let vi = VarInfo::new(
1391+
subr_t,
1392+
muty,
1393+
Visibility::DUMMY_PUBLIC,
1394+
VarKind::Builtin,
1395+
None,
1396+
ContextKind::Dummy,
1397+
None,
1398+
AbsLocation::unknown(),
1399+
);
1400+
return Ok(vi);
1401+
}
1402+
}
13871403
for patch in self.find_patches_of(obj.ref_t()) {
13881404
if let Some(vi) = patch.get_current_scope_non_param(&attr_name.name) {
13891405
self.validate_visibility(attr_name, vi, input, namespace)?;
@@ -1508,7 +1524,7 @@ impl Context {
15081524
}
15091525
return Ok(method.method_info.clone());
15101526
}
1511-
Triple::Err(err) => {
1527+
Triple::Err(err) if ERG_MODE => {
15121528
return Err(err);
15131529
}
15141530
_ => {}
@@ -3551,6 +3567,7 @@ impl Context {
35513567
// if all methods have the same return type, the minimum type (has biggest param types) is selected
35523568
// e.g. [Float -> Bool, Int -> Bool] => Float -> Bool
35533569
// REVIEW: should [Int -> Bool, Str -> Bool] => (Str or Int) -> Bool?
3570+
// -> get_union_method_type
35543571
if let Some(min) = self.min_type(candidates.iter().map(|mp| &mp.method_info.t)) {
35553572
let min_pair = candidates
35563573
.iter()
@@ -3574,6 +3591,55 @@ impl Context {
35743591
))
35753592
}
35763593

3594+
// (Int -> Bool, Float -> Bool) => Int or Float -> Bool
3595+
fn get_union_method_type(&self, candidates: &[MethodPair]) -> Option<Type> {
3596+
let fst = candidates.first()?;
3597+
let mut kind = fst.method_info.t.subr_kind()?;
3598+
let mut union_nds = fst.method_info.t.non_default_params()?.clone();
3599+
let mut union_var = fst.method_info.t.var_params().cloned();
3600+
let mut union_ds = fst.method_info.t.default_params()?.clone();
3601+
let mut union_kw_var = fst.method_info.t.kw_var_params().cloned();
3602+
let mut union_return = fst.method_info.t.return_t()?.clone();
3603+
for cand in candidates.iter().skip(1) {
3604+
kind = kind | cand.method_info.t.subr_kind()?;
3605+
for (union, r) in union_nds
3606+
.iter_mut()
3607+
.zip(cand.method_info.t.non_default_params()?)
3608+
{
3609+
*union.typ_mut() = self.union(union.typ(), r.typ());
3610+
}
3611+
if let Some((union, r)) = union_var.as_mut().zip(cand.method_info.t.var_params()) {
3612+
*union.typ_mut() = self.union(union.typ(), r.typ());
3613+
}
3614+
for (union, r) in union_ds
3615+
.iter_mut()
3616+
.zip(cand.method_info.t.default_params()?)
3617+
{
3618+
*union.typ_mut() = self.union(union.typ(), r.typ());
3619+
}
3620+
if let Some((union, r)) = union_kw_var
3621+
.as_mut()
3622+
.zip(cand.method_info.t.kw_var_params())
3623+
{
3624+
*union.typ_mut() = self.union(union.typ(), r.typ());
3625+
}
3626+
union_return = self.union(&union_return, cand.method_info.t.return_t()?);
3627+
}
3628+
let subr = Type::Subr(SubrType::new(
3629+
kind,
3630+
union_nds,
3631+
union_var,
3632+
union_ds,
3633+
union_kw_var,
3634+
union_return,
3635+
));
3636+
if subr.has_qvar() {
3637+
Some(subr.quantify())
3638+
} else {
3639+
Some(subr)
3640+
}
3641+
}
3642+
35773643
/// Infer the receiver type from the attribute name.
35783644
/// Returns an error if multiple candidates are found. If nothing is found, returns None.
35793645
fn get_attr_type_by_name(
@@ -3595,6 +3661,20 @@ impl Context {
35953661
}
35963662
}
35973663

3664+
fn get_union_attr_type_by_name(&self, attr: &Identifier) -> Option<Type> {
3665+
if let Some(candidates) = self.method_to_traits.get(attr.inspect()) {
3666+
return self.get_union_method_type(candidates);
3667+
}
3668+
if let Some(candidates) = self.method_to_classes.get(attr.inspect()) {
3669+
return self.get_union_method_type(candidates);
3670+
}
3671+
if let Some(outer) = self.get_outer_scope_or_builtins() {
3672+
outer.get_union_attr_type_by_name(attr)
3673+
} else {
3674+
None
3675+
}
3676+
}
3677+
35983678
fn _get_gen_t_require_attr_t<'a>(
35993679
&'a self,
36003680
gen: &'a GenTypeObj,

crates/erg_compiler/ty/mod.rs

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -973,6 +973,16 @@ impl From<TokenKind> for SubrKind {
973973
}
974974
}
975975

976+
impl BitOr for SubrKind {
977+
type Output = Self;
978+
fn bitor(self, rhs: Self) -> Self {
979+
match (self, rhs) {
980+
(Self::Func, Self::Func) => Self::Func,
981+
_ => Self::Proc,
982+
}
983+
}
984+
}
985+
976986
impl SubrKind {
977987
pub const fn arrow(&self) -> Str {
978988
match self {
@@ -2488,6 +2498,17 @@ impl Type {
24882498
}
24892499
}
24902500

2501+
pub fn subr_kind(&self) -> Option<SubrKind> {
2502+
match self {
2503+
Self::FreeVar(fv) if fv.is_linked() => fv.crack().subr_kind(),
2504+
Self::Subr(subr) => Some(subr.kind),
2505+
Self::Refinement(refine) => refine.t.subr_kind(),
2506+
Self::Quantified(quant) => quant.subr_kind(),
2507+
Self::And(l, r) => l.subr_kind().and_then(|k| r.subr_kind().map(|k2| k | k2)),
2508+
_ => None,
2509+
}
2510+
}
2511+
24912512
pub fn is_quantified_subr(&self) -> bool {
24922513
match self {
24932514
Self::FreeVar(fv) if fv.is_linked() => fv.crack().is_quantified_subr(),

0 commit comments

Comments
 (0)