Skip to content

Commit

Permalink
Add an is_reference_type::<T>() builtin. (#1121)
Browse files Browse the repository at this point in the history
Returns false for copy types (unit, bool, byte, uint), true otherwise.

This is a temporary measure to assist in getting the stdlib richer now,
i.e., to make decisions regarding ASM block instructions based on
generic types, but isn't reliable once we have optimisations which could
invalidate compile time assertions like this.  A 'reference type' may
not always be used by reference if optimisations allow a value to be
passed by value.

`is_reference_type::<T>()` closely resembles `size_of::<T>()` in both
syntax and semantics and so I've combined them into a builtin 'type
property' expression and split the `size_of_value(expr)` expression
into its own thing.
  • Loading branch information
otrho authored Apr 2, 2022
1 parent 5bb1bc3 commit d6bfac2
Show file tree
Hide file tree
Showing 16 changed files with 322 additions and 145 deletions.
93 changes: 69 additions & 24 deletions sway-core/src/asm_generation/expression/mod.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,10 @@
use super::*;
use crate::{
asm_lang::*,
parse_tree::{CallPath, Literal},
parse_tree::{BuiltinProperty, CallPath, Literal},
semantic_analysis::{
ast_node::{
SizeOfVariant, TypedAsmRegisterDeclaration, TypedCodeBlock, TypedEnumVariant,
TypedExpressionVariant,
TypedAsmRegisterDeclaration, TypedCodeBlock, TypedEnumVariant, TypedExpressionVariant,
},
TypedExpression,
},
Expand Down Expand Up @@ -340,8 +339,26 @@ pub(crate) fn convert_expression_to_asm(
namespace,
register_sequencer,
),
TypedExpressionVariant::SizeOf { variant } => convert_size_of_expression_to_asm(
variant,
TypedExpressionVariant::TypeProperty { property, type_id } => match property {
BuiltinProperty::SizeOfType => convert_size_of_to_asm(
None,
type_id,
namespace,
return_register,
register_sequencer,
exp.span.clone(),
),
BuiltinProperty::IsRefType => convert_is_ref_type_to_asm(
type_id,
namespace,
return_register,
register_sequencer,
exp.span.clone(),
),
},
TypedExpressionVariant::SizeOfValue { expr } => convert_size_of_to_asm(
Some(expr),
&expr.return_type,
namespace,
return_register,
register_sequencer,
Expand Down Expand Up @@ -422,34 +439,62 @@ fn convert_literal_to_asm(
}]
}

fn convert_size_of_expression_to_asm(
variant: &SizeOfVariant,
fn convert_is_ref_type_to_asm(
type_id: &TypeId,
namespace: &mut AsmNamespace,
return_register: &VirtualRegister,
register_sequencer: &mut RegisterSequencer,
span: Span,
) -> CompileResult<Vec<Op>> {
let mut warnings = vec![];
let warnings = vec![];
let mut errors = vec![];
let mut asm_buf = vec![];
let type_id = match variant {
SizeOfVariant::Val(exp) => {
asm_buf.push(Op::new_comment("size_of_val".to_string()));
let mut ops = check!(
convert_expression_to_asm(exp, namespace, return_register, register_sequencer),
vec![],
warnings,
errors
);
asm_buf.append(&mut ops);
exp.return_type
let mut asm_buf = vec![Op::new_comment("is_ref_type".to_string())];
let ty = match resolve_type(*type_id, &span) {
Ok(o) => o,
Err(e) => {
errors.push(e.into());
return err(warnings, errors);
}
SizeOfVariant::Type(ty) => {
asm_buf.push(Op::new_comment("size_of".to_string()));
*ty
};
let is_ref_type = match ty.is_copy_type(&span) {
Ok(is_copy) => !is_copy,
Err(e) => {
errors.push(e);
return err(warnings, errors);
}
};
let ty = match resolve_type(type_id, &span) {
let mut ops = convert_literal_to_asm(
&Literal::Boolean(is_ref_type),
namespace,
return_register,
register_sequencer,
span,
);
asm_buf.append(&mut ops);
ok(asm_buf, warnings, errors)
}

fn convert_size_of_to_asm(
expr: Option<&TypedExpression>,
type_id: &TypeId,
namespace: &mut AsmNamespace,
return_register: &VirtualRegister,
register_sequencer: &mut RegisterSequencer,
span: Span,
) -> CompileResult<Vec<Op>> {
let mut warnings = vec![];
let mut errors = vec![];
let mut asm_buf = vec![Op::new_comment("size_of_val".to_string())];
if let Some(expr) = expr {
let mut ops = check!(
convert_expression_to_asm(expr, namespace, return_register, register_sequencer),
vec![],
warnings,
errors
);
asm_buf.append(&mut ops);
}
let ty = match resolve_type(*type_id, &span) {
Ok(o) => o,
Err(e) => {
errors.push(e.into());
Expand Down
9 changes: 8 additions & 1 deletion sway-core/src/asm_generation/expression/subfield.rs
Original file line number Diff line number Diff line change
Expand Up @@ -160,7 +160,14 @@ pub(crate) fn convert_subfield_to_asm(
}
};

asm_buf.push(if resolved_type_of_this_field.is_copy_type() {
let field_has_copy_type = match resolved_type_of_this_field.is_copy_type(&span) {
Ok(is_copy) => is_copy,
Err(e) => {
errors.push(e);
return err(warnings, errors);
}
};
asm_buf.push(if field_has_copy_type {
let offset_in_words = match VirtualImmediate12::new(offset_in_words, span.clone()) {
Ok(o) => o,
Err(e) => {
Expand Down
10 changes: 9 additions & 1 deletion sway-core/src/asm_generation/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1430,7 +1430,15 @@ fn ret_or_retd_value(
);
}

if main_func_ret_ty.is_copy_type() {
let main_func_ret_ty_is_copy_type = match main_func_ret_ty.is_copy_type(&func.return_type_span)
{
Ok(is_copy) => is_copy,
Err(e) => {
errors.push(e);
return err(warnings, errors);
}
};
if main_func_ret_ty_is_copy_type {
asm_buf.push(Op {
owning_span: None,
opcode: Either::Left(VirtualOp::RET(return_register)),
Expand Down
32 changes: 15 additions & 17 deletions sway-core/src/control_flow_analysis/dead_code_analysis.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@ use crate::{
parse_tree::{CallPath, Visibility},
semantic_analysis::{
ast_node::{
SizeOfVariant, TypedAbiDeclaration, TypedCodeBlock, TypedConstantDeclaration,
TypedDeclaration, TypedEnumDeclaration, TypedExpression, TypedExpressionVariant,
TypedAbiDeclaration, TypedCodeBlock, TypedConstantDeclaration, TypedDeclaration,
TypedEnumDeclaration, TypedExpression, TypedExpressionVariant,
TypedFunctionDeclaration, TypedReassignment, TypedReturnStatement,
TypedStructDeclaration, TypedStructExpressionField, TypedTraitDeclaration,
TypedVariableDeclaration, TypedWhileLoop,
Expand Down Expand Up @@ -1006,21 +1006,19 @@ fn connect_expression(
}
Ok(vec![this_ix])
}
SizeOf { variant } => match variant {
SizeOfVariant::Type(_) => Ok(vec![]),
SizeOfVariant::Val(exp) => {
let exp = connect_expression(
&(*exp).expression,
graph,
leaves,
exit_node,
"size_of",
tree_type,
exp.span.clone(),
)?;
Ok(exp)
}
},
TypeProperty { .. } => Ok(Vec::new()),
SizeOfValue { expr } => {
let expr = connect_expression(
&(*expr).expression,
graph,
leaves,
exit_node,
"size_of",
tree_type,
expr.span.clone(),
)?;
Ok(expr)
}
a => {
println!("Unimplemented: {:?}", a);
Err(CompileError::Unimplemented(
Expand Down
48 changes: 23 additions & 25 deletions sway-core/src/optimize.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ use std::collections::HashMap;

use crate::{
asm_generation::from_ir::ir_type_size_in_bytes,
parse_tree::{AsmOp, AsmRegister, LazyOp, Literal, Visibility},
parse_tree::{AsmOp, AsmRegister, BuiltinProperty, LazyOp, Literal, Visibility},
semantic_analysis::{ast_node::*, *},
type_engine::*,
};
Expand Down Expand Up @@ -637,33 +637,31 @@ impl FnCompiler {
let span_md_idx = MetadataIndex::from_span(context, &access.span());
self.compile_storage_access(context, &access.fields, &access.ix, span_md_idx)
}
TypedExpressionVariant::SizeOf { variant } => {
match variant {
SizeOfVariant::Type(type_id) => {
let ir_type = convert_resolved_typeid_no_span(context, &type_id)?;
Ok(Constant::get_uint(
context,
64,
ir_type_size_in_bytes(context, &ir_type),
None,
))
}
SizeOfVariant::Val(exp) => {
let ir_type =
convert_resolved_typeid(context, &exp.return_type, &exp.span)?;

// Compile the expression in case of side-effects but ignore its value.
self.compile_expression(context, *exp)?;

Ok(Constant::get_uint(
context,
64,
ir_type_size_in_bytes(context, &ir_type),
None,
))
TypedExpressionVariant::TypeProperty { property, type_id } => {
let ir_type = convert_resolved_typeid_no_span(context, &type_id)?;
match property {
BuiltinProperty::SizeOfType => Ok(Constant::get_uint(
context,
64,
ir_type_size_in_bytes(context, &ir_type),
None,
)),
BuiltinProperty::IsRefType => {
Ok(Constant::get_bool(context, !ir_type.is_copy_type(), None))
}
}
}
TypedExpressionVariant::SizeOfValue { expr } => {
// Compile the expression in case of side-effects but ignore its value.
let ir_type = convert_resolved_typeid(context, &expr.return_type, &expr.span)?;
self.compile_expression(context, *expr)?;
Ok(Constant::get_uint(
context,
64,
ir_type_size_in_bytes(context, &ir_type),
None,
))
}
}
}

Expand Down
32 changes: 23 additions & 9 deletions sway-core/src/parse_tree/expression/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -173,13 +173,20 @@ pub enum Expression {
exp: Box<Expression>,
span: Span,
},
SizeOfType {
BuiltinGetTypeProperty {
builtin: BuiltinProperty,
type_name: TypeInfo,
type_span: Span,
span: Span,
},
}

#[derive(Debug, Clone, PartialEq)]
pub enum BuiltinProperty {
SizeOfType,
IsRefType,
}

#[derive(Debug, Clone)]
pub enum DelayedResolutionVariant {
StructField(DelayedStructFieldResolution),
Expand Down Expand Up @@ -312,7 +319,7 @@ impl Expression {
StorageAccess { span, .. } => span,
IfLet { span, .. } => span,
SizeOfVal { span, .. } => span,
SizeOfType { span, .. } => span,
BuiltinGetTypeProperty { span, .. } => span,
})
.clone()
}
Expand Down Expand Up @@ -1266,8 +1273,8 @@ impl Expression {
warnings,
errors
),
Rule::size_of_expr => check!(
parse_size_of_expr(expr, config),
Rule::built_in_expr => check!(
parse_built_in_expr(expr, config),
return err(warnings, errors),
warnings,
errors
Expand Down Expand Up @@ -1439,7 +1446,7 @@ pub(crate) fn parse_array_index(
)
}

pub(crate) fn parse_size_of_expr(
pub(crate) fn parse_built_in_expr(
item: Pair<Rule>,
config: Option<&BuildConfig>,
) -> CompileResult<ParserLifter<Expression>> {
Expand Down Expand Up @@ -1471,10 +1478,12 @@ pub(crate) fn parse_size_of_expr(
value: exp,
}
}
Rule::size_of_type_expr => {
// The size_of_type and is_ref_type_expr rules have identical grammar apart from the
// keyword.
Rule::size_of_type_expr | Rule::is_ref_type_expr => {
let mut inner_iter = size_of.into_inner();
let _keyword = inner_iter.next();
let elem = inner_iter.next().expect("guarenteed by grammar");
let keyword = inner_iter.next().expect("guaranteed by grammar");
let elem = inner_iter.next().expect("guaranteed by grammar");
let type_span = Span {
span: elem.as_span(),
path: config.map(|c| c.path()),
Expand All @@ -1485,7 +1494,12 @@ pub(crate) fn parse_size_of_expr(
warnings,
errors
);
let exp = Expression::SizeOfType {
let exp = Expression::BuiltinGetTypeProperty {
builtin: match keyword.as_str() {
"size_of" => BuiltinProperty::SizeOfType,
"is_reference_type" => BuiltinProperty::IsRefType,
_otherwise => unreachable!("unexpected built in keyword: {keyword}"),
},
type_name,
type_span,
span,
Expand Down
Loading

0 comments on commit d6bfac2

Please sign in to comment.