Skip to content

Commit

Permalink
feat: re-add qualifier handling
Browse files Browse the repository at this point in the history
  • Loading branch information
jac3km4 committed Dec 7, 2022
1 parent 36588c2 commit 42c4820
Show file tree
Hide file tree
Showing 8 changed files with 158 additions and 101 deletions.
8 changes: 4 additions & 4 deletions compiler/src/autobox.rs
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ impl<'ctx, 'id> Autobox<'ctx, 'id> {
self.apply(rhs);
match (&**lhs, Boxable::from_infer_type(typ, self.type_repo)) {
(Expr::Member(_, Member::Field(field, _), _), Some(prim))
if requires_boxing(self.type_repo.get_field(field).unwrap(), self.type_repo) =>
if requires_boxing(&self.type_repo.get_field(field).unwrap().typ, self.type_repo) =>
{
**rhs = self.box_primitive(&prim, mem::take(rhs));
}
Expand All @@ -62,7 +62,7 @@ impl<'ctx, 'id> Autobox<'ctx, 'id> {
}
Expr::Member(obj, Member::Field(field, typ), _) => {
self.apply(obj);
if requires_boxing(self.type_repo.get_field(field).unwrap(), self.type_repo) {
if requires_boxing(&self.type_repo.get_field(field).unwrap().typ, self.type_repo) {
let typ = typ.clone();
self.try_unbox_expr(expr, &typ);
}
Expand Down Expand Up @@ -244,7 +244,7 @@ impl<'id> Boxable<'id> {

fn from_type_id(id: TypeId<'id>, repo: &TypeRepo<'id>) -> Option<Boxable<'id>> {
match repo.get_type(id)? {
DataType::Class(class) if class.is_struct => Some(Boxable::Struct(id)),
DataType::Class(class) if class.flags.is_struct() => Some(Boxable::Struct(id)),
DataType::Enum(_) => Some(Boxable::Enum(id)),
_ => None,
}
Expand All @@ -270,7 +270,7 @@ fn requires_boxing<'id>(typ: &Type<'id>, repo: &TypeRepo<'id>) -> bool {
match typ {
Type::Top | Type::Var(_) => true,
Type::Data(data) => match repo.get_type(data.id).unwrap() {
DataType::Class(c) => !c.is_struct,
DataType::Class(c) => !c.flags.is_struct(),
&DataType::Builtin { is_unboxed, .. } => !is_unboxed,
&DataType::Enum(_) => false,
},
Expand Down
4 changes: 2 additions & 2 deletions compiler/src/codegen.rs
Original file line number Diff line number Diff line change
Expand Up @@ -257,7 +257,7 @@ impl<'ctx, 'id> CodeGen<'ctx, 'id> {
Expr::Member(expr, member, _) => match member {
Member::Field(field, _) => {
let dt = self.repo.get_type(field.owner()).unwrap().as_class().unwrap();
if dt.is_struct {
if dt.flags.is_struct() {
self.emit(Instr::StructField(*self.db.fields.get(&field).unwrap()));
self.assemble(*expr, pool, cache);
} else {
Expand All @@ -282,7 +282,7 @@ impl<'ctx, 'id> CodeGen<'ctx, 'id> {
Expr::New(typ, args, _) => {
let class = self.repo.get_type(typ.id).unwrap().as_class().unwrap();
let &tt = self.db.classes.get(&typ.id).unwrap();
if class.is_struct {
if class.flags.is_struct() {
self.emit(Instr::Construct(args.len() as u8, tt));
for arg in args.into_vec() {
self.assemble(arg, pool, cache);
Expand Down
6 changes: 4 additions & 2 deletions compiler/src/codegen/builders.rs
Original file line number Diff line number Diff line change
Expand Up @@ -277,7 +277,7 @@ impl TypeCache {
pool: &mut ConstantPool,
) -> PoolIndex<PoolType> {
if matches!(typ, Type::Var(_) | Type::Bottom | Type::Top)
|| matches!(typ, Type::Data(data) if matches!(repo.get_type(data.id), Some(DataType::Class(class)) if !class.is_struct))
|| matches!(typ, Type::Data(data) if matches!(repo.get_type(data.id), Some(DataType::Class(class)) if !class.flags.is_struct()))
{
let data = Type::Data(Parameterized::new(predef::REF, Rc::new([typ.clone()])));
self.alloc_type_unwrapped(&data, repo, pool)
Expand Down Expand Up @@ -336,7 +336,9 @@ fn serialize_type<'id>(typ: &Type<'id>, repo: &TypeRepo<'id>, unwrapped: bool) -
DataType::Builtin { .. } if !typ.args.is_empty() => {
Either::Right(str_fmt!("{}:{}", typ.id, serialize_type(&typ.args[0], repo, false)))
}
DataType::Class(class) if !class.is_struct && !unwrapped => Either::Right(str_fmt!("ref:{}", typ.id)),
DataType::Class(class) if !class.flags.is_struct() && !unwrapped => {
Either::Right(str_fmt!("ref:{}", typ.id))
}
_ => Either::Left(typ.id.as_str()),
},
Type::Prim(prim) => Either::Left(prim.into()),
Expand Down
148 changes: 93 additions & 55 deletions compiler/src/compiler.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ use redscript::ast::{Expr, Seq, SourceAst, Span};
use redscript::bundle::{ConstantPool, PoolIndex};
use redscript::bytecode::Intrinsic;
use redscript::definition::{
AnyDefinition, Class as PoolClass, ClassFlags, Enum as PoolEnum, Field as PoolField, Function as PoolFunction, FunctionFlags, ParameterFlags, Type as PoolType, Visibility
AnyDefinition, Class as PoolClass, ClassFlags, Enum as PoolEnum, Field as PoolField, FieldFlags, Function as PoolFunction, FunctionFlags, ParameterFlags, Type as PoolType, Visibility
};
use redscript::Str;
use sequence_trie::SequenceTrie;
Expand All @@ -20,7 +20,7 @@ use crate::codegen::builders::{ClassBuilder, EnumBuilder, FieldBuilder, Function
use crate::codegen::{names, CodeGen, LocalIndices};
use crate::error::{CompileError, CompileResult, ParseError, TypeError, Unsupported};
use crate::parser::{
self, AnnotationKind, ClassSource, EnumSource, FunctionSource, Import, MemberSource, ModulePath, ParameterSource, Qualifier, SourceEntry, SourceModule
self, AnnotationKind, ClassSource, EnumSource, FunctionSource, Import, MemberSource, ModulePath, ParameterSource, Qualifier, Qualifiers, SourceEntry, SourceModule
};
use crate::scoped_map::ScopedMap;
use crate::source_map::Files;
Expand Down Expand Up @@ -243,8 +243,7 @@ impl<'id> Compiler<'id> {
fields: FieldMap::default(),
methods: FuncMap::default(),
statics: FuncMap::default(),
is_abstract: class.qualifiers.contain(Qualifier::Abstract),
is_struct,
flags: get_class_flags(&class.qualifiers).with_is_struct(is_struct),
span: Some(class.span),
};
let mut methods = vec![];
Expand All @@ -253,26 +252,16 @@ impl<'id> Compiler<'id> {
for member in class.members {
match member {
MemberSource::Method(method) => {
let is_static = method.decl.qualifiers.contain(Qualifier::Static);
if !is_static && is_struct {
self.reporter.report(CompileError::Unsupported(
Unsupported::NonStaticStructMember,
method.decl.span,
));
}
let is_final = is_static || method.decl.qualifiers.contain(Qualifier::Final);
let is_native = method.decl.qualifiers.contain(Qualifier::Native);
let flags =
get_function_flags(&method.decl.qualifiers).with_has_body(method.body.is_some());
self.validate_method(data_type.flags, flags, method.decl.span);

let res = self.preprocess_function(&method, types, &type_vars);
let Some((env, typ)) = self.reporter.unwrap_err(res) else { continue };
let index = if is_static {
data_type.statics.add(method.decl.name.clone(), typ, is_final, true)
let index = if flags.is_static() {
data_type.statics.add(method.decl.name.clone(), typ, flags)
} else {
data_type.methods.add(
method.decl.name.clone(),
typ,
is_final,
method.body.is_some() || is_native,
)
data_type.methods.add(method.decl.name.clone(), typ, flags)
};
if let Some(body) = method.body {
methods.push(CompileBody {
Expand All @@ -281,15 +270,17 @@ impl<'id> Compiler<'id> {
env,
parameters: method.parameters,
body,
is_static,
is_static: flags.is_static(),
});
}
}
MemberSource::Field(field) => {
let flags = get_field_flags(&field.declaration.qualifiers);
self.validate_field(data_type.flags, flags, field.declaration.span);
let env = TypeEnv::new(types, &type_vars);
let res = env.resolve_type(&field.type_).with_span(field.declaration.span);
let Some(typ) = self.reporter.unwrap_err(res) else { continue };
data_type.fields.add(field.declaration.name, typ);
data_type.fields.add(field.declaration.name, Field::new(typ, flags));
}
}
}
Expand All @@ -305,6 +296,7 @@ impl<'id> Compiler<'id> {
Ok(None)
}
SourceEntry::Function(func) => {
let flags = get_function_flags(&func.decl.qualifiers);
let (env, typ) = self.preprocess_function(&func, types, &ScopedMap::default())?;
let name = ScopedName::new(func.decl.name.clone(), path.clone());
for ann in &func.decl.annotations {
Expand All @@ -317,11 +309,7 @@ impl<'id> Compiler<'id> {
_ => {}
}
}
let is_native = func.decl.qualifiers.contain(Qualifier::Native);
let index = self
.repo
.globals_mut()
.add(name, typ, true, func.body.is_some() || is_native);
let index = self.repo.globals_mut().add(name, typ, flags);
let global = if let Ok(intrinsic) = Intrinsic::from_str(&func.decl.name) {
Global::Intrinsic(index.overload(), intrinsic)
} else {
Expand Down Expand Up @@ -350,6 +338,32 @@ impl<'id> Compiler<'id> {
}
}

fn validate_method(&mut self, type_flags: ClassFlags, method_flags: FunctionFlags, span: Span) {
if method_flags.is_native() && !type_flags.is_native() {
self.reporter
.report(CompileError::Unsupported(Unsupported::NativeInNonNative, span));
}
if !method_flags.is_static() && type_flags.is_struct() {
self.reporter
.report(CompileError::Unsupported(Unsupported::NonStaticStructMember, span));
}
if method_flags.is_final() && !method_flags.has_body() && !method_flags.is_native() {
self.reporter
.report(CompileError::Unsupported(Unsupported::FinalWithoutBody, span));
}
if method_flags.has_body() && method_flags.is_native() {
self.reporter
.report(CompileError::Unsupported(Unsupported::NativeWithBody, span));
}
}

fn validate_field(&mut self, type_flags: ClassFlags, field_flags: FieldFlags, span: Span) {
if field_flags.is_native() && !type_flags.is_native() {
self.reporter
.report(CompileError::Unsupported(Unsupported::NativeInNonNative, span));
}
}

fn process_replacement(
&self,
replace: Str,
Expand Down Expand Up @@ -552,6 +566,18 @@ impl<'id> Compiler<'id> {
for module in &self.compile_queue {
for item in &module.items {
let ModuleItem::Class(owner, this, _, funcs) = item else { continue };
let Some(base) = self
.repo
.get_type(*owner)
.and_then(DataType::as_class)
.and_then(|class| class.extends.as_ref())
.and_then(|typ| self.repo.get_type(typ.id))
.and_then(DataType::as_class) else { continue };
if let Some(span) = base.span.filter(|_| base.flags.is_final()) {
self.reporter
.report(CompileError::Unsupported(Unsupported::ExtendingFinalClass, span));
}

for func in funcs {
let &CompileBody { index, is_static, .. } = func;
if is_static {
Expand Down Expand Up @@ -582,14 +608,14 @@ impl<'id> Compiler<'id> {

for entry in class.methods.iter() {
let mid = MethodId::new(typ, entry.index);
if !entry.function.is_implemented {
if !entry.function.is_implemented() {
this_unimplemented.insert(mid);
} else if let Some(base) = method_to_base.get(&mid) {
this_unimplemented.remove(base);
}
}

if !class.is_abstract && !this_unimplemented.is_empty() {
if !class.flags.is_abstract() && !this_unimplemented.is_empty() {
for method in &this_unimplemented {
let name = self.repo.get_method_name(method).unwrap();
self.reporter
Expand Down Expand Up @@ -628,7 +654,7 @@ impl<'id> Compiler<'id> {
.upper_iter(owner)
.skip(1)
.flat_map(|(type_id, class)| class.methods.by_name(name).map(move |res| (type_id, res)))
.filter(|(_, e)| e.function.typ.params.len() == typ.params.len())
.filter(|(_, e)| e.function.typ.params.len() == typ.params.len() && !e.function.flags.is_final())
.filter(|(id, e)| {
let base = this.clone().instantiate_as(*id, repo).unwrap();
let vars = repo
Expand Down Expand Up @@ -726,7 +752,8 @@ impl<'id> CompilationOutputs<'id> {
let fields = class_type.fields.iter().map(|entry| {
FieldBuilder::builder()
.name(entry.name.clone())
.typ(entry.typ.clone())
.typ(entry.field.typ.clone())
.flags(entry.field.flags)
.build()
});
let methods = chain!(
Expand All @@ -744,11 +771,7 @@ impl<'id> CompilationOutputs<'id> {
.name(id.as_str())
.fields(fields)
.methods(methods)
.flags(
ClassFlags::new()
.with_is_abstract(class_type.is_abstract)
.with_is_struct(class_type.is_struct),
)
.flags(class_type.flags)
.build()
.commit_as(class_idx, base.unwrap_or(PoolIndex::UNDEFINED), repo, pool, cache);

Expand Down Expand Up @@ -832,7 +855,7 @@ impl<'id> CompilationDb<'id> {
let name = pool.def_name(idx).unwrap();
let field = pool.field(idx).unwrap();
let typ = CompilationDb::load_type(field.type_, pool, interner);
let index = fields.add(name.clone(), typ);
let index = fields.add(name.clone(), Field::new(typ, field.flags));
self.fields.insert(FieldId::new(owner, index), idx);
}

Expand All @@ -842,16 +865,10 @@ impl<'id> CompilationDb<'id> {
let method = pool.function(pool_idx).unwrap();
let (short_name, signature, ftyp) = CompilationDb::load_function(pool_idx, pool, interner);
if method.flags.is_static() {
let index = statics.add_with_signature(short_name, signature, ftyp, true, true);
let index = statics.add_with_signature(short_name, signature, ftyp, method.flags);
self.statics.insert(MethodId::new(owner, index), pool_idx);
} else {
let index = methods.add_with_signature(
short_name,
signature,
ftyp,
method.flags.is_final(),
method.flags.has_body() || method.flags.is_native(),
);
let index = methods.add_with_signature(short_name, signature, ftyp, method.flags);
self.methods.insert(MethodId::new(owner, index), pool_idx);
}
}
Expand All @@ -867,8 +884,7 @@ impl<'id> CompilationDb<'id> {
fields,
methods,
statics,
is_abstract: class.flags.is_abstract(),
is_struct: class.flags.is_struct(),
flags: class.flags,
span: None,
}
}
Expand Down Expand Up @@ -965,13 +981,10 @@ impl<'id> CompilationResources<'id> {
}
AnyDefinition::Function(fun) if def.parent.is_undefined() => {
let (name, sig, ftyp) = CompilationDb::load_function(idx.cast(), pool, interner);
let id = type_repo.globals_mut().add_with_signature(
ScopedName::top_level(name),
sig,
ftyp,
true,
fun.flags.has_body() || fun.flags.is_native(),
);
let id =
type_repo
.globals_mut()
.add_with_signature(ScopedName::top_level(name), sig, ftyp, fun.flags);
db.globals.insert(GlobalId::new(id), idx.cast());
}
AnyDefinition::Enum(_) => {
Expand Down Expand Up @@ -1071,3 +1084,28 @@ impl<'id> ModuleMap<'id> {
.insert_owned(typ.as_parts().map(Str::from), ImportItem::Type(typ));
}
}

fn get_function_flags(qualifiers: &Qualifiers) -> FunctionFlags {
let is_static = qualifiers.contain(Qualifier::Static);
FunctionFlags::new()
.with_is_native(qualifiers.contain(Qualifier::Native))
.with_is_callback(qualifiers.contain(Qualifier::Callback))
.with_is_final(is_static || qualifiers.contain(Qualifier::Final))
.with_is_quest(qualifiers.contain(Qualifier::Quest))
.with_is_static(is_static)
}

fn get_class_flags(qualifiers: &Qualifiers) -> ClassFlags {
let is_import_only = qualifiers.contain(Qualifier::ImportOnly);
ClassFlags::new()
.with_is_native(is_import_only || qualifiers.contain(Qualifier::Native))
.with_is_import_only(is_import_only)
.with_is_abstract(qualifiers.contain(Qualifier::Abstract))
.with_is_final(qualifiers.contain(Qualifier::Final))
}

fn get_field_flags(qualifiers: &Qualifiers) -> FieldFlags {
FieldFlags::new()
.with_is_native(qualifiers.contain(Qualifier::Native))
.with_is_persistent(qualifiers.contain(Qualifier::Persistent))
}
12 changes: 10 additions & 2 deletions compiler/src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ pub enum CompileError<'id> {
UnresolvedImport(Box<[Str]>, Span),
#[error("virtual method {0} must be implemented")]
UnimplementedMethod(Str, Span),
#[error("unsupported operation: {0}")]
#[error("{0} is not supported")]
Unsupported(Unsupported, Span),
}

Expand Down Expand Up @@ -141,6 +141,14 @@ pub enum Unsupported {
CustomClassConstructor,
#[error("defining a non-static member on a struct")]
NonStaticStructMember,
#[error("method replacement without a body")]
#[error("defining a method replacement without a body")]
ReplacementWithoutBody,
#[error("defining a final method without a body")]
FinalWithoutBody,
#[error("defining a native method with a body")]
NativeWithBody,
#[error("extending a final class")]
ExtendingFinalClass,
#[error("defining a native member in a non-native type")]
NativeInNonNative,
}
Loading

0 comments on commit 42c4820

Please sign in to comment.