Compare commits

..

No commits in common. "fef4b2a5ce5c0685f597695ef5c640ce2f1625ce" and "e328e44c9a6a424d916e628960b1f6eb56cd916f" have entirely different histories.

3 changed files with 93 additions and 119 deletions

View File

@ -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>> {
@ -302,31 +302,6 @@ impl<'a> Inferencer<'a> {
if let Some(value) = value { if let Some(value) = value {
self.check_expr(value, defined_identifiers)?; self.check_expr(value, defined_identifiers)?;
self.should_have_value(value)?; self.should_have_value(value)?;
// 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.
if let Some(ret_ty) = value.custom {
if [
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)).not() {
// Explicitly allow ellipsis as a return value, as the type of the ellipsis is contextually
// inferred and just generates an unconditional assertion
if matches!(value.node, ExprKind::Constant { value: Constant::Ellipsis, .. }).not() {
return Err(HashSet::from([
format!(
"return value of type {} must be a primitive",
self.unifier.stringify(ret_ty),
),
]))
}
}
}
} }
Ok(true) Ok(true)
} }

View File

@ -23,8 +23,8 @@ class A:
def get_a(self) -> int32: def get_a(self) -> int32:
return self.a return self.a
# def get_b(self) -> B: def get_b(self) -> B:
# return self.b return self.b
def run() -> int32: def run() -> int32:

View File

@ -21,8 +21,8 @@ def run() -> int32:
test_uint32() test_uint32()
test_int64() test_int64()
test_uint64() test_uint64()
# test_A() test_A()
# test_B() test_B()
return 0 return 0
def test_int32(): def test_int32():
@ -173,92 +173,91 @@ def test_uint64():
a >>= uint32(b) a >>= uint32(b)
output_uint64(a) output_uint64(a)
# FIXME Fix returning objects of non-primitive types; Currently this is disabled in the function checker class A:
# class A: a: int32
# a: int32 def __init__(self, a: int32):
# def __init__(self, a: int32): self.a = a
# self.a = a
# def __add__(self, other: A) -> A:
# def __add__(self, other: A) -> A: output_int32(self.a + other.a)
# output_int32(self.a + other.a) return A(self.a + other.a)
# return A(self.a + other.a)
# def __sub__(self, other: A) -> A:
# def __sub__(self, other: A) -> A: output_int32(self.a - other.a)
# output_int32(self.a - other.a) return A(self.a - other.a)
# return A(self.a - other.a)
# def test_A():
# def test_A(): a = A(17)
# a = A(17) b = A(3)
# b = A(3)
# c = a + b
# c = a + b # fail due to alloca in __add__ function
# # fail due to alloca in __add__ function # output_int32(c.a)
# # output_int32(c.a)
# a += b
# a += b # fail due to alloca in __add__ function
# # fail due to alloca in __add__ function # output_int32(a.a)
# # output_int32(a.a)
# a = A(17)
# a = A(17) b = A(3)
# b = A(3) d = a - b
# d = a - b # fail due to alloca in __add__ function
# # fail due to alloca in __add__ function # output_int32(c.a)
# # output_int32(c.a)
# a -= b
# a -= b # fail due to alloca in __add__ function
# # fail due to alloca in __add__ function # output_int32(a.a)
# # output_int32(a.a)
# a = A(17)
# a = A(17) b = A(3)
# b = A(3) a.__add__(b)
# a.__add__(b) a.__sub__(b)
# a.__sub__(b)
#
# class B:
# class B: a: int32
# a: int32 def __init__(self, a: int32):
# def __init__(self, a: int32): self.a = a
# self.a = a
# def __add__(self, other: B) -> B:
# def __add__(self, other: B) -> B: output_int32(self.a + other.a)
# output_int32(self.a + other.a) return B(self.a + other.a)
# return B(self.a + other.a)
# def __sub__(self, other: B) -> B:
# def __sub__(self, other: B) -> B: output_int32(self.a - other.a)
# output_int32(self.a - other.a) return B(self.a - other.a)
# return B(self.a - other.a)
# def __iadd__(self, other: B) -> B:
# def __iadd__(self, other: B) -> B: output_int32(self.a + other.a + 24)
# output_int32(self.a + other.a + 24) return B(self.a + other.a + 24)
# return B(self.a + other.a + 24)
# def __isub__(self, other: B) -> B:
# def __isub__(self, other: B) -> B: output_int32(self.a - other.a - 24)
# output_int32(self.a - other.a - 24) return B(self.a - other.a - 24)
# return B(self.a - other.a - 24)
# def test_B():
# def test_B(): a = B(17)
# a = B(17) b = B(3)
# b = B(3)
# c = a + b
# c = a + b # fail due to alloca in __add__ function
# # fail due to alloca in __add__ function # output_int32(c.a)
# # output_int32(c.a)
# a += b
# a += b # fail due to alloca in __add__ function
# # fail due to alloca in __add__ function # output_int32(a.a)
# # output_int32(a.a)
# a = B(17)
# a = B(17) b = B(3)
# b = B(3) d = a - b
# d = a - b # fail due to alloca in __add__ function
# # fail due to alloca in __add__ function # output_int32(c.a)
# # output_int32(c.a)
# a -= b
# a -= b # fail due to alloca in __add__ function
# # fail due to alloca in __add__ function # output_int32(a.a)
# # output_int32(a.a)
# a = B(17)
# a = B(17) b = B(3)
# b = B(3) a.__add__(b)
# a.__add__(b) a.__sub__(b)
# a.__sub__(b)