Skip to content

Commit 948642d

Browse files
committed
Add universal implicits
1 parent 2446809 commit 948642d

File tree

5 files changed

+195
-50
lines changed

5 files changed

+195
-50
lines changed

vhdl_lang/src/analysis/expression.rs

Lines changed: 48 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,9 @@ impl<'a> From<DisambiguatedType<'a>> for ExpressionType<'a> {
6161
pub(super) struct TypeMatcher<'c, 'a> {
6262
// Allow implicit type conversion from universal real/integer to other integer types
6363
implicit_type_conversion: bool,
64+
65+
// Allow implicit type conversion from abstract types to universal real/integer
66+
implicit_type_conversion_from_universal: bool,
6467
context: &'c AnalyzeContext<'a>,
6568
}
6669

@@ -73,18 +76,28 @@ impl<'c, 'a> TypeMatcher<'c, 'a> {
7376
match ttyp.kind() {
7477
Type::Integer => types.match_type(self.context.universal_integer()),
7578
Type::Real => types.match_type(self.context.universal_real()),
76-
Type::Universal(UniversalType::Integer) => match types {
77-
ExpressionType::Unambiguous(typ) => typ.base().is_any_integer(),
78-
ExpressionType::Ambiguous(types) => {
79-
types.iter().any(|typ| typ.is_any_integer())
79+
Type::Universal(UniversalType::Integer)
80+
if self.implicit_type_conversion_from_universal =>
81+
{
82+
match types {
83+
ExpressionType::Unambiguous(typ) => typ.base().is_any_integer(),
84+
ExpressionType::Ambiguous(types) => {
85+
types.iter().any(|typ| typ.is_any_integer())
86+
}
87+
_ => false,
88+
}
89+
}
90+
Type::Universal(UniversalType::Real)
91+
if self.implicit_type_conversion_from_universal =>
92+
{
93+
match types {
94+
ExpressionType::Unambiguous(typ) => typ.base().is_any_real(),
95+
ExpressionType::Ambiguous(types) => {
96+
types.iter().any(|typ| typ.is_any_real())
97+
}
98+
_ => false,
8099
}
81-
_ => false,
82-
},
83-
Type::Universal(UniversalType::Real) => match types {
84-
ExpressionType::Unambiguous(typ) => typ.base().is_any_real(),
85-
ExpressionType::Ambiguous(types) => types.iter().any(|typ| typ.is_any_real()),
86-
_ => false,
87-
},
100+
}
88101
_ => false,
89102
}
90103
} else {
@@ -112,32 +125,28 @@ impl<'c, 'a> TypeMatcher<'c, 'a> {
112125
pub fn disambiguate_by_assoc_types(
113126
&self,
114127
actual_types: &[Option<ExpressionType<'a>>],
115-
candidates: &[ResolvedCall<'a>],
116-
) -> Vec<OverloadedEnt<'a>> {
117-
candidates
118-
.iter()
119-
.filter(|resolved| {
120-
actual_types.iter().enumerate().all(|(idx, actual_type)| {
121-
if let Some(actual_type) = actual_type {
122-
self.is_possible(actual_type, resolved.formals[idx].type_mark().base())
123-
} else {
124-
true
125-
}
126-
})
128+
candidates: &mut Vec<ResolvedCall<'a>>,
129+
) {
130+
candidates.retain(|resolved| {
131+
actual_types.iter().enumerate().all(|(idx, actual_type)| {
132+
if let Some(actual_type) = actual_type {
133+
self.is_possible(actual_type, resolved.formals[idx].type_mark().base())
134+
} else {
135+
true
136+
}
127137
})
128-
.map(|resolved| resolved.subpgm)
129-
.collect()
138+
})
130139
}
131140

132141
pub fn disambiguate_op_by_return_type(
133142
&self,
134-
candidates: &mut Vec<OverloadedEnt<'a>>,
143+
candidates: &mut Vec<impl AsRef<OverloadedEnt<'a>>>,
135144
ttyp: Option<TypeEnt<'a>>, // Optional target type constraint
136145
) {
137146
let tbase = ttyp.map(|ttyp| ttyp.base());
138147
candidates.retain(|ent| {
139148
if let Some(tbase) = tbase {
140-
self.can_be_target_type(ent.return_type().unwrap(), tbase)
149+
self.can_be_target_type(ent.as_ref().return_type().unwrap(), tbase)
141150
} else {
142151
true
143152
}
@@ -149,20 +158,30 @@ impl<'a> AnalyzeContext<'a> {
149158
pub fn strict_matcher(&self) -> TypeMatcher<'_, 'a> {
150159
TypeMatcher {
151160
implicit_type_conversion: false,
161+
implicit_type_conversion_from_universal: false,
162+
context: self,
163+
}
164+
}
165+
166+
pub fn any_matcher(&self) -> TypeMatcher<'_, 'a> {
167+
TypeMatcher {
168+
implicit_type_conversion: true,
169+
implicit_type_conversion_from_universal: true,
152170
context: self,
153171
}
154172
}
155173

156174
pub fn implicit_matcher(&self) -> TypeMatcher<'_, 'a> {
157175
TypeMatcher {
158176
implicit_type_conversion: true,
177+
implicit_type_conversion_from_universal: false,
159178
context: self,
160179
}
161180
}
162181

163182
// Returns true if the expression types is possible given the target type
164183
pub fn is_possible(&self, types: &ExpressionType<'a>, ttyp: BaseType<'a>) -> bool {
165-
self.implicit_matcher().is_possible(types, ttyp)
184+
self.any_matcher().is_possible(types, ttyp)
166185
}
167186

168187
pub fn common_type(&self, typ1: BaseType<'a>, typ2: BaseType<'a>) -> Option<BaseType<'a>> {
@@ -189,7 +208,7 @@ impl<'a> AnalyzeContext<'a> {
189208
}
190209

191210
pub fn can_be_target_type(&self, typ: TypeEnt<'a>, ttyp: BaseType<'a>) -> bool {
192-
self.implicit_matcher().can_be_target_type(typ, ttyp)
211+
self.any_matcher().can_be_target_type(typ, ttyp)
193212
}
194213

195214
pub fn expr_unknown_ttyp(

vhdl_lang/src/analysis/overloaded.rs

Lines changed: 72 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -165,11 +165,24 @@ impl<'a> Disambiguated<'a> {
165165
}
166166
}
167167

168+
#[derive(Clone)]
168169
pub(super) struct ResolvedCall<'a> {
169170
pub subpgm: OverloadedEnt<'a>,
170171
pub formals: Vec<ResolvedFormal<'a>>,
171172
}
172173

174+
impl<'a> AsRef<OverloadedEnt<'a>> for OverloadedEnt<'a> {
175+
fn as_ref(&self) -> &OverloadedEnt<'a> {
176+
self
177+
}
178+
}
179+
180+
impl<'a> AsRef<OverloadedEnt<'a>> for ResolvedCall<'a> {
181+
fn as_ref(&self) -> &OverloadedEnt<'a> {
182+
&self.subpgm
183+
}
184+
}
185+
173186
impl<'a> AnalyzeContext<'a> {
174187
/// Typecheck one overloaded call where the exact subprogram is known
175188
pub fn check_call(
@@ -296,12 +309,12 @@ impl<'a> AnalyzeContext<'a> {
296309

297310
let actual_types = self.actual_types(scope, assocs, diagnostics)?;
298311

299-
let ok_assoc_types = self
300-
.implicit_matcher()
301-
.disambiguate_by_assoc_types(&actual_types, &ok_formals);
312+
let mut ok_assoc_types = ok_formals.clone();
313+
self.implicit_matcher()
314+
.disambiguate_by_assoc_types(&actual_types, &mut ok_assoc_types);
302315

303316
if ok_assoc_types.len() == 1 {
304-
let ent = ok_assoc_types[0];
317+
let ent = ok_assoc_types[0].subpgm;
305318
self.check_call(scope, call_pos, ent, assocs, diagnostics)?;
306319
return Ok(Disambiguated::Unambiguous(ent));
307320
} else if ok_assoc_types.is_empty() {
@@ -312,24 +325,47 @@ impl<'a> AnalyzeContext<'a> {
312325
return Err(EvalError::Unknown);
313326
}
314327

315-
if let SubprogramKind::Function(rtyp) = kind {
328+
let ok_return_type = if let SubprogramKind::Function(rtyp) = kind {
316329
let mut ok_return_type = ok_assoc_types.clone();
317330
self.implicit_matcher()
318331
.disambiguate_op_by_return_type(&mut ok_return_type, rtyp);
319332

320333
// Only one candidate matches type profile, check it
321334
if ok_return_type.len() == 1 {
322-
let ent = ok_return_type[0];
335+
let ent = ok_return_type[0].subpgm;
323336
self.check_call(scope, call_pos, ent, assocs, diagnostics)?;
324337
return Ok(Disambiguated::Unambiguous(ent));
325338
} else if ok_return_type.is_empty() {
326-
diagnostics.push(Diagnostic::could_not_resolve(call_name, ok_assoc_types));
339+
diagnostics.push(Diagnostic::could_not_resolve(
340+
call_name,
341+
ok_assoc_types.into_iter().map(|resolved| resolved.subpgm),
342+
));
327343
return Err(EvalError::Unknown);
328344
}
329-
Ok(Disambiguated::Ambiguous(ok_return_type))
345+
ok_return_type
330346
} else {
331-
Ok(Disambiguated::Ambiguous(ok_assoc_types))
347+
ok_assoc_types
348+
};
349+
350+
let mut strict_ok_assoc_types = ok_return_type.clone();
351+
self.strict_matcher()
352+
.disambiguate_by_assoc_types(&actual_types, &mut strict_ok_assoc_types);
353+
354+
if strict_ok_assoc_types.len() == 1 {
355+
let ent = strict_ok_assoc_types[0].subpgm;
356+
self.check_call(scope, call_pos, ent, assocs, diagnostics)?;
357+
return Ok(Disambiguated::Unambiguous(ent));
358+
} else if strict_ok_assoc_types.is_empty() {
359+
// Do not disambiguate away to emtpy result
360+
strict_ok_assoc_types = ok_return_type.clone();
332361
}
362+
363+
Ok(Disambiguated::Ambiguous(
364+
strict_ok_assoc_types
365+
.into_iter()
366+
.map(|resolved| resolved.subpgm)
367+
.collect(),
368+
))
333369
}
334370

335371
pub fn disambiguate_no_actuals(
@@ -667,4 +703,31 @@ function myfun(arg1 : integer) return character;
667703
],
668704
)
669705
}
706+
707+
#[test]
708+
fn ambiguous_builtin_favors_non_implicit_conversion_unary() {
709+
let test = TestSetup::new();
710+
let code = test.snippet("to_string(0)");
711+
712+
let uint_to_string =
713+
test.lookup_implicit_of(test.ctx().universal_integer().into(), "to_string");
714+
715+
assert_eq!(
716+
test.disambiguate(&code, None, &mut NoDiagnostics),
717+
Some(Disambiguated::Unambiguous(uint_to_string))
718+
);
719+
}
720+
721+
#[test]
722+
fn ambiguous_builtin_favors_non_implicit_conversion_binary() {
723+
let test = TestSetup::new();
724+
let code = test.snippet("minimum(0, integer'(0))");
725+
726+
let minimum = test.lookup_implicit_of(test.ctx().integer(), "minimum");
727+
728+
assert_eq!(
729+
test.disambiguate(&code, None, &mut NoDiagnostics),
730+
Some(Disambiguated::Unambiguous(minimum))
731+
);
732+
}
670733
}

vhdl_lang/src/analysis/root.rs

Lines changed: 19 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -707,18 +707,25 @@ impl DesignRoot {
707707
let scope = root_scope.nested().in_package_declaration();
708708

709709
{
710-
let univ_int_unary_minus = context
711-
.symmetric_unary(Operator::Minus, context.universal_integer().into());
712-
let univ_real_unary_minus = context
713-
.symmetric_unary(Operator::Minus, context.universal_real().into());
714-
715-
// Add unary minus as it is used in the standard package definition
716-
unsafe {
717-
arena.add_implicit(universal.integer, univ_int_unary_minus);
718-
scope.add(univ_int_unary_minus, &mut diagnostics);
719-
arena.add_implicit(universal.real, univ_real_unary_minus);
720-
scope.add(univ_real_unary_minus, &mut diagnostics);
721-
};
710+
for ent in context.universal_implicits(
711+
UniversalType::Integer,
712+
context.universal_integer().into(),
713+
) {
714+
unsafe {
715+
arena.add_implicit(universal.integer, ent);
716+
};
717+
scope.add(ent, &mut diagnostics);
718+
}
719+
720+
for ent in context.universal_implicits(
721+
UniversalType::Real,
722+
context.universal_real().into(),
723+
) {
724+
unsafe {
725+
arena.add_implicit(universal.real, ent);
726+
};
727+
scope.add(ent, &mut diagnostics);
728+
}
722729
}
723730

724731
for decl in std_package.decl.iter_mut() {

vhdl_lang/src/analysis/standard.rs

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -704,6 +704,51 @@ impl<'a> AnalyzeContext<'a> {
704704
.chain(self.comparators(typ).into_iter())
705705
}
706706

707+
pub fn universal_implicits(
708+
&self,
709+
kind: UniversalType,
710+
typ: TypeEnt<'a>,
711+
) -> impl Iterator<Item = EntRef<'a>> {
712+
[
713+
self.minimum(typ),
714+
self.maximum(typ),
715+
self.create_to_string(typ),
716+
self.symmetric_unary(Operator::Minus, typ),
717+
self.symmetric_unary(Operator::Plus, typ),
718+
self.symmetric_binary(Operator::Plus, typ),
719+
self.symmetric_binary(Operator::Minus, typ),
720+
// 9.2.7 Multiplying operators
721+
self.symmetric_binary(Operator::Times, typ),
722+
self.symmetric_binary(Operator::Div, typ),
723+
// 9.2.8 Miscellaneous operators
724+
self.symmetric_unary(Operator::Abs, typ),
725+
self.binary(
726+
Operator::Pow,
727+
typ,
728+
typ,
729+
self.universal_integer().into(),
730+
typ,
731+
),
732+
]
733+
.into_iter()
734+
.chain(
735+
if kind == UniversalType::Integer {
736+
Some(
737+
[
738+
self.symmetric_binary(Operator::Mod, typ),
739+
self.symmetric_binary(Operator::Rem, typ),
740+
]
741+
.into_iter(),
742+
)
743+
} else {
744+
None
745+
}
746+
.into_iter()
747+
.flatten(),
748+
)
749+
.chain(self.comparators(typ).into_iter())
750+
}
751+
707752
pub fn physical_implicits(&self, typ: TypeEnt<'a>) -> impl Iterator<Item = EntRef<'a>> {
708753
let integer = self.integer();
709754
let real = self.real();

vhdl_lang/src/analysis/tests/mod.rs

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ mod visibility;
2727
use std::cell::RefCell;
2828

2929
pub use self::util::*;
30+
use crate::ast::Designator;
3031
use crate::ast::UnitId;
3132
pub use crate::data::Diagnostic;
3233
use crate::data::DiagnosticHandler;
@@ -115,6 +116,16 @@ impl<'a> TestSetup<'a> {
115116
}
116117
}
117118

119+
pub fn lookup_implicit_of(&'a self, typ: TypeEnt<'a>, name: &str) -> OverloadedEnt<'a> {
120+
let des = Designator::Identifier(self.root.symbol_utf8(name));
121+
let ent = typ
122+
.implicits
123+
.iter()
124+
.find(|ent| ent.designator() == &des)
125+
.unwrap();
126+
OverloadedEnt::from_any(ent).unwrap()
127+
}
128+
118129
pub fn lookup_type(&'a self, sym: &str) -> TypeEnt<'a> {
119130
TypeEnt::from_any(self.lookup(sym)).unwrap()
120131
}

0 commit comments

Comments
 (0)