forked from M-Labs/nac3
core: Allow tuple of primitives to be returned
This commit is contained in:
parent
fef4b2a5ce
commit
d6302b6ec8
|
@ -3,7 +3,7 @@ use crate::typecheck::typedef::TypeEnum;
|
||||||
use super::type_inferencer::Inferencer;
|
use super::type_inferencer::Inferencer;
|
||||||
use super::typedef::Type;
|
use super::typedef::Type;
|
||||||
use nac3parser::ast::{self, Constant, Expr, ExprKind, Operator::{LShift, RShift}, Stmt, StmtKind, StrRef};
|
use nac3parser::ast::{self, Constant, Expr, ExprKind, Operator::{LShift, RShift}, Stmt, StmtKind, StrRef};
|
||||||
use std::{collections::HashSet, iter::once, ops::Not};
|
use std::{collections::HashSet, iter::once};
|
||||||
|
|
||||||
impl<'a> Inferencer<'a> {
|
impl<'a> Inferencer<'a> {
|
||||||
fn should_have_value(&mut self, expr: &Expr<Option<Type>>) -> Result<(), HashSet<String>> {
|
fn should_have_value(&mut self, expr: &Expr<Option<Type>>) -> Result<(), HashSet<String>> {
|
||||||
|
@ -208,6 +208,27 @@ impl<'a> Inferencer<'a> {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Check that the return value is a non-`alloca` type, effectively only allowing primitive types.
|
||||||
|
///
|
||||||
|
/// This is a workaround preventing the caller from using a variable `alloca`-ed in the body, which
|
||||||
|
/// is freed when the function returns.
|
||||||
|
fn check_return_value_ty(&mut self, ret_ty: Type) -> bool {
|
||||||
|
match &*self.unifier.get_ty_immutable(ret_ty) {
|
||||||
|
TypeEnum::TObj { .. } => {
|
||||||
|
[
|
||||||
|
self.primitives.int32,
|
||||||
|
self.primitives.int64,
|
||||||
|
self.primitives.uint32,
|
||||||
|
self.primitives.uint64,
|
||||||
|
self.primitives.float,
|
||||||
|
self.primitives.bool,
|
||||||
|
].iter().any(|allowed_ty| self.unifier.unioned(ret_ty, *allowed_ty))
|
||||||
|
}
|
||||||
|
TypeEnum::TTuple { ty } => ty.iter().all(|t| self.check_return_value_ty(*t)),
|
||||||
|
_ => false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// check statements for proper identifier def-use and return on all paths
|
// check statements for proper identifier def-use and return on all paths
|
||||||
fn check_stmt(
|
fn check_stmt(
|
||||||
&mut self,
|
&mut self,
|
||||||
|
@ -307,24 +328,19 @@ impl<'a> Inferencer<'a> {
|
||||||
// This is a workaround preventing the caller from using a variable `alloca`-ed in the body, which
|
// This is a workaround preventing the caller from using a variable `alloca`-ed in the body, which
|
||||||
// is freed when the function returns.
|
// is freed when the function returns.
|
||||||
if let Some(ret_ty) = value.custom {
|
if let Some(ret_ty) = value.custom {
|
||||||
if [
|
// Explicitly allow ellipsis as a return value, as the type of the ellipsis is contextually
|
||||||
self.primitives.int32,
|
// inferred and just generates an unconditional assertion
|
||||||
self.primitives.int64,
|
if matches!(value.node, ExprKind::Constant { value: Constant::Ellipsis, .. }) {
|
||||||
self.primitives.uint32,
|
return Ok(true)
|
||||||
self.primitives.uint64,
|
}
|
||||||
self.primitives.float,
|
|
||||||
self.primitives.bool,
|
if !self.check_return_value_ty(ret_ty) {
|
||||||
].iter().any(|allowed_ty| self.unifier.unioned(ret_ty, *allowed_ty)).not() {
|
return Err(HashSet::from([
|
||||||
// Explicitly allow ellipsis as a return value, as the type of the ellipsis is contextually
|
format!(
|
||||||
// inferred and just generates an unconditional assertion
|
"return value of type {} must be a primitive of a tuple of primitives",
|
||||||
if matches!(value.node, ExprKind::Constant { value: Constant::Ellipsis, .. }).not() {
|
self.unifier.stringify(ret_ty),
|
||||||
return Err(HashSet::from([
|
),
|
||||||
format!(
|
]))
|
||||||
"return value of type {} must be a primitive",
|
|
||||||
self.unifier.stringify(ret_ty),
|
|
||||||
),
|
|
||||||
]))
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue