Skip to content

Commit

Permalink
Merge 837b70a into e6f7ce7
Browse files Browse the repository at this point in the history
  • Loading branch information
xunilrj authored Jun 24, 2024
2 parents e6f7ce7 + 837b70a commit 3cc7e6d
Show file tree
Hide file tree
Showing 41 changed files with 590 additions and 274 deletions.
222 changes: 222 additions & 0 deletions sway-core/src/ir_generation/function.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1721,6 +1721,228 @@ impl<'eng> FnCompiler<'eng> {
Ok(increase_len(&mut s.current_block, context, len, 8 - offset))
}

// Grow the buffer if needed
fn grow_if_needed(
s: &mut FnCompiler<'_>,
context: &mut Context,
ptr: Value,
cap: Value,
len: Value,
needed_size: Value,
) -> (Value, Value) {
assert!(ptr.get_type(context).unwrap().is_ptr(context));
assert!(cap.get_type(context).unwrap().is_uint64(context));

let ptr_u8 = Type::new_ptr(context, Type::get_uint8(context));

// merge block has two arguments: ptr, cap
let merge_block = s.function.create_block(context, None);
let merge_block_ptr = Value::new_argument(
context,
BlockArgument {
block: merge_block,
idx: 0,
ty: ptr_u8,
},
);
merge_block.add_arg(context, merge_block_ptr);
let merge_block_cap = Value::new_argument(
context,
BlockArgument {
block: merge_block,
idx: 1,
ty: Type::get_uint64(context),
},
);
merge_block.add_arg(context, merge_block_cap);

let true_block_begin = s.function.create_block(context, None);
let false_block_begin = s.function.create_block(context, None);

// if len + needed_size > cap
let needed_cap = s.current_block.append(context).binary_op(
BinaryOpKind::Add,
len,
needed_size,
);
let needs_realloc = s.current_block.append(context).cmp(
Predicate::GreaterThan,
needed_cap,
cap,
);
s.current_block.append(context).conditional_branch(
needs_realloc,
true_block_begin,
false_block_begin,
vec![],
vec![],
);

// needs realloc block
// new_cap = cap * 2
// aloc new_cap
// mcp hp old_ptr len
// hp: ptr u8
s.current_block = true_block_begin;
let u8 = Type::get_uint8(context);
let ptr_u8 = Type::new_ptr(context, u8);

let two = Constant::new_uint(context, 64, 2);
let two = Value::new_constant(context, two);
let new_cap =
s.current_block
.append(context)
.binary_op(BinaryOpKind::Mul, cap, two);

let new_ptr = s.current_block.append(context).asm_block(
vec![
AsmArg {
name: Ident::new_no_span("new_cap".into()),
initializer: Some(new_cap),
},
AsmArg {
name: Ident::new_no_span("old_ptr".into()),
initializer: Some(ptr),
},
AsmArg {
name: Ident::new_no_span("len".into()),
initializer: Some(len),
},
],
vec![
AsmInstruction {
op_name: Ident::new_no_span("aloc".into()),
args: vec![Ident::new_no_span("new_cap".into())],
immediate: None,
metadata: None,
},
AsmInstruction {
op_name: Ident::new_no_span("mcp".into()),
args: vec![
Ident::new_no_span("hp".into()),
Ident::new_no_span("old_ptr".into()),
Ident::new_no_span("len".into()),
],
immediate: None,
metadata: None,
},
],
ptr_u8,
Some(Ident::new_no_span("hp".into())),
);

s.current_block
.append(context)
.branch(merge_block, vec![new_ptr, new_cap]);

// dont need realloc block
s.current_block = false_block_begin;
s.current_block
.append(context)
.branch(merge_block, vec![ptr, cap]);

s.current_block = merge_block;

assert!(merge_block_ptr.get_type(context).unwrap().is_ptr(context));
assert!(merge_block_cap
.get_type(context)
.unwrap()
.is_uint64(context));

(merge_block_ptr, merge_block_cap)
}

fn to_constant(
_s: &mut FnCompiler<'_>,
context: &mut Context,
needed_size: u64,
) -> Value {
let needed_size = Constant::new_uint(context, 64, needed_size);
Value::new_constant(context, needed_size)
}

let (ptr, cap) = match &*item_type {
TypeInfo::Boolean => {
let needed_size = to_constant(self, context, 1);
grow_if_needed(self, context, ptr, cap, len, needed_size)
}
TypeInfo::UnsignedInteger(IntegerBits::Eight) => {
let needed_size = to_constant(self, context, 1);
grow_if_needed(self, context, ptr, cap, len, needed_size)
}
TypeInfo::UnsignedInteger(IntegerBits::Sixteen) => {
let needed_size = to_constant(self, context, 2);
grow_if_needed(self, context, ptr, cap, len, needed_size)
}
TypeInfo::UnsignedInteger(IntegerBits::ThirtyTwo) => {
let needed_size = to_constant(self, context, 4);
grow_if_needed(self, context, ptr, cap, len, needed_size)
}
TypeInfo::UnsignedInteger(IntegerBits::SixtyFour) => {
let needed_size = to_constant(self, context, 8);
grow_if_needed(self, context, ptr, cap, len, needed_size)
}
TypeInfo::UnsignedInteger(IntegerBits::V256) | TypeInfo::B256 => {
let needed_size = to_constant(self, context, 32);
grow_if_needed(self, context, ptr, cap, len, needed_size)
}
TypeInfo::StringArray(string_len) => {
let needed_size = to_constant(self, context, 8 + string_len.val() as u64);
grow_if_needed(self, context, ptr, cap, len, needed_size)
}
TypeInfo::StringSlice | TypeInfo::RawUntypedSlice => {
let uint64 = Type::get_uint64(context);
let u64_u64_type = Type::new_struct(context, vec![uint64, uint64]);

// convert "item" to { u64, u64 }
let item = self.current_block.append(context).asm_block(
vec![AsmArg {
name: Ident::new_no_span("item".into()),
initializer: Some(item),
}],
vec![],
u64_u64_type,
Some(Ident::new_no_span("item".into())),
);

// save item to local _anon
let name = self.lexical_map.insert_anon();
let item_local = self
.function
.new_local_var(context, name, u64_u64_type, None, false)
.map_err(|ir_error| {
CompileError::InternalOwned(ir_error.to_string(), Span::dummy())
})?;
let ptr_to_local_item =
self.current_block.append(context).get_local(item_local);
self.current_block
.append(context)
.store(ptr_to_local_item, item);

// _anon.0 = ptr
// let ptr = self.current_block.append(context).get_elem_ptr_with_idx(
// item_local_value,
// uint64,
// 0,
// );
// let ptr = self.current_block.append(context).load(ptr);
// let ptr_u8 = Type::new_ptr(context, Type::get_uint8(context));
// let ptr = self.current_block.append(context).int_to_ptr(ptr, ptr_u8);

// _anon.1 = len
let needed_size = self.current_block.append(context).get_elem_ptr_with_idx(
ptr_to_local_item,
uint64,
1,
);
let needed_size = self.current_block.append(context).load(needed_size);

grow_if_needed(self, context, ptr, cap, len, needed_size)
}
_ => return Err(CompileError::EncodingUnsupportedType { span: item_span }),
};

// Append the value into the buffer
let new_len = match &*item_type {
TypeInfo::Boolean => {
assert!(item.get_type(context).unwrap().is_bool(context));
Expand Down
12 changes: 9 additions & 3 deletions sway-core/src/language/ty/declaration/function.rs
Original file line number Diff line number Diff line change
Expand Up @@ -602,9 +602,15 @@ impl TyFunctionSig {
}

pub fn is_concrete(&self, engines: &Engines) -> bool {
self.return_type.is_concrete(engines)
&& self.parameters.iter().all(|p| p.is_concrete(engines))
&& self.type_parameters.iter().all(|p| p.is_concrete(engines))
self.return_type.is_concrete(engines, IncludeNumeric::No)
&& self
.parameters
.iter()
.all(|p| p.is_concrete(engines, IncludeNumeric::No))
&& self
.type_parameters
.iter()
.all(|p| p.is_concrete(engines, IncludeNumeric::No))
}

/// Returns a String representing the function.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -247,21 +247,25 @@ fn type_check_encode_append(
};

// only supported types
match &*engines.te().get(item_type) {
TypeInfo::Boolean
| TypeInfo::UnsignedInteger(IntegerBits::Eight)
| TypeInfo::UnsignedInteger(IntegerBits::Sixteen)
| TypeInfo::UnsignedInteger(IntegerBits::ThirtyTwo)
| TypeInfo::UnsignedInteger(IntegerBits::SixtyFour)
| TypeInfo::UnsignedInteger(IntegerBits::V256)
| TypeInfo::B256
| TypeInfo::StringArray(_)
| TypeInfo::StringSlice
| TypeInfo::RawUntypedSlice => {}
_ => {
return Err(handler.emit_err(CompileError::EncodingUnsupportedType { span: item_span }))
}
};
if item_type.is_concrete(engines, IncludeNumeric::Yes) {
match &*engines.te().get(item_type) {
TypeInfo::Boolean
| TypeInfo::UnsignedInteger(IntegerBits::Eight)
| TypeInfo::UnsignedInteger(IntegerBits::Sixteen)
| TypeInfo::UnsignedInteger(IntegerBits::ThirtyTwo)
| TypeInfo::UnsignedInteger(IntegerBits::SixtyFour)
| TypeInfo::UnsignedInteger(IntegerBits::V256)
| TypeInfo::B256
| TypeInfo::StringArray(_)
| TypeInfo::StringSlice
| TypeInfo::RawUntypedSlice => {}
_ => {
return Err(
handler.emit_err(CompileError::EncodingUnsupportedType { span: item_span })
)
}
};
}

let kind = ty::TyIntrinsicFunctionKind {
kind,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,11 +46,25 @@ pub(crate) fn type_check_method_application(
.by_ref()
.with_help_text("")
.with_type_annotation(type_engine.insert(engines, TypeInfo::Unknown, None));

// Ignore errors in method parameters
// On the second pass we will throw the errors if they persist.
let arg_handler = Handler::default();
let arg_opt = ty::TyExpression::type_check(&arg_handler, ctx, arg).ok();

// Check this type needs a second pass
let has_errors = arg_handler.has_errors();
let is_not_concrete = arg_opt
.as_ref()
.map(|x| {
x.return_type
.extract_inner_types(engines, IncludeSelf::Yes)
.iter()
.any(|x| !x.is_concrete(engines, IncludeNumeric::Yes))
})
.unwrap_or_default();
let needs_second_pass = has_errors || is_not_concrete;

if index == 0 {
// We want to emit errors in the self parameter and ignore TraitConstraintNotSatisfied with Placeholder
// which may be recoverable on the second pass.
Expand All @@ -66,7 +80,8 @@ pub(crate) fn type_check_method_application(
});
handler.append(arg_handler);
}
args_opt_buf.push_back((arg_opt, has_errors));

args_opt_buf.push_back((arg_opt, needs_second_pass));
}

// resolve the method name to a typed function declaration and type_check
Expand Down Expand Up @@ -114,21 +129,22 @@ pub(crate) fn type_check_method_application(
} else {
index
};

let tid = method
.parameters
.get(param_index)
.unwrap()
.type_argument
.type_id;

// This arg_opt is None because it failed in the first pass.
// We now try to type check it again, this time with the type annotation.
let ctx = ctx
.by_ref()
.with_help_text(
"Function application argument type must match function parameter type.",
)
.with_type_annotation(
method
.parameters
.get(param_index)
.unwrap()
.type_argument
.type_id,
);
.with_type_annotation(tid);
args_buf.push_back(
ty::TyExpression::type_check(handler, ctx, arg)
.unwrap_or_else(|err| ty::TyExpression::error(err, span.clone(), engines)),
Expand Down
22 changes: 18 additions & 4 deletions sway-core/src/type_system/id.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,11 @@ pub enum IncludeSelf {
No,
}

pub enum IncludeNumeric {
Yes,
No,
}

/// A identifier to uniquely refer to our type terms
#[derive(PartialEq, Eq, Hash, Clone, Copy, Ord, PartialOrd, Debug)]
pub struct TypeId(usize);
Expand Down Expand Up @@ -480,17 +485,26 @@ impl TypeId {
}))
}

pub(crate) fn is_concrete(&self, engines: &Engines) -> bool {
pub(crate) fn is_concrete(&self, engines: &Engines, include_numeric: IncludeNumeric) -> bool {
let nested_types = (*self).extract_nested_types(engines);
!nested_types.into_iter().any(|x| {
matches!(
!nested_types.into_iter().any(|x| match include_numeric {
IncludeNumeric::Yes => matches!(
x,
TypeInfo::UnknownGeneric { .. }
| TypeInfo::Custom { .. }
| TypeInfo::Placeholder(..)
| TypeInfo::TraitType { .. }
| TypeInfo::TypeParam(..)
)
| TypeInfo::Numeric
),
IncludeNumeric::No => matches!(
x,
TypeInfo::UnknownGeneric { .. }
| TypeInfo::Custom { .. }
| TypeInfo::Placeholder(..)
| TypeInfo::TraitType { .. }
| TypeInfo::TypeParam(..)
),
})
}

Expand Down
Loading

0 comments on commit 3cc7e6d

Please sign in to comment.