Skip to content

Commit

Permalink
Add raw_ptr type (FuelLabs#2828)
Browse files Browse the repository at this point in the history
This PR:
- Introduces the `raw_ptr` type.
- Changes RawVec's `ptr` field from `u64` to `raw_ptr`.
- Disallows returning `raw_ptr`s (and aggregate types that contain it)
from being returned from `main()` fns.


![image](https://user-images.githubusercontent.com/412180/195128463-1809a7f4-3964-419c-9eac-03df799dedc3.png)

---

Note to @sezna:

On our meeting we discussed `checks.rs`, which led me to this
implementation:
FuelLabs@090a4d1

That didn't work because `raw_ptr`s are converted to
`sway_ir::irtype::Type::Uint(64)`s so there wasn't a way to
differentiate them. I've reverted that and went with this instead:
FuelLabs@fc6bf34

Co-authored-by: Andrew Cann <[email protected]>
  • Loading branch information
AlicanC and canndrew authored Oct 25, 2022
1 parent 8ae44c2 commit 13ffbc3
Show file tree
Hide file tree
Showing 56 changed files with 344 additions and 221 deletions.
2 changes: 2 additions & 0 deletions forc-pkg/src/pkg.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1833,6 +1833,8 @@ pub fn dependency_namespace(
}
}

namespace.star_import_with_reexports(&[CORE, PRELUDE].map(Ident::new_no_span), &[]);

if has_std_dep(graph, node) {
namespace.star_import_with_reexports(&[STD, PRELUDE].map(Ident::new_no_span), &[]);
}
Expand Down
1 change: 1 addition & 0 deletions sway-core/src/ir_generation/convert.rs
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,7 @@ fn convert_resolved_type(
create_tuple_aggregate(context, new_fields).map(Type::Struct)?
}
}
TypeInfo::RawUntypedPtr => Type::Uint(64),

// Unsupported types which shouldn't exist in the AST after type checking and
// monomorphisation.
Expand Down
5 changes: 2 additions & 3 deletions sway-core/src/ir_generation/function.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,6 @@ use sway_ir::{Context, *};
use sway_types::{
constants,
ident::Ident,
integer_bits::IntegerBits,
span::{Span, Spanned},
state::StateIndex,
};
Expand Down Expand Up @@ -584,11 +583,11 @@ impl FnCompiler {
// Validate that the val_exp is of the right type. We couldn't do it
// earlier during type checking as the type arguments may not have been resolved.
let val_ty = to_typeinfo(val_exp.return_type, &span)?;
if val_ty != TypeInfo::UnsignedInteger(IntegerBits::SixtyFour) {
if val_ty != TypeInfo::RawUntypedPtr {
return Err(CompileError::IntrinsicUnsupportedArgType {
name: kind.to_string(),
span,
hint: Hint::new("This argument must be u64".to_string()),
hint: Hint::new("This argument must be raw_ptr".to_string()),
});
}
let key_value = self.compile_expression(context, md_mgr, key_exp)?;
Expand Down
19 changes: 18 additions & 1 deletion sway-core/src/language/ty/program.rs
Original file line number Diff line number Diff line change
Expand Up @@ -180,8 +180,25 @@ impl TyProgram {
name: mains.last().unwrap().name.clone(),
});
}
// A script must not return a `raw_ptr`
let main_func = mains.remove(0);
let nested_types = check!(
look_up_type_id(main_func.return_type)
.extract_nested_types(&main_func.return_type_span),
vec![],
warnings,
errors
);
if nested_types
.iter()
.any(|ty| matches!(ty, TypeInfo::RawUntypedPtr))
{
errors.push(CompileError::PointerReturnNotAllowedInMain {
span: main_func.return_type_span.clone(),
});
}
TyProgramKind::Script {
main_function: mains.remove(0),
main_function: main_func,
declarations,
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -185,8 +185,10 @@ impl ty::TyIntrinsicFunctionKind {
warnings,
errors
);
let is_valid_arg_ty = matches!(arg_ty, TypeInfo::UnsignedInteger(_))
|| matches!(arg_ty, TypeInfo::Boolean);
let is_valid_arg_ty = matches!(
arg_ty,
TypeInfo::UnsignedInteger(_) | TypeInfo::Boolean | TypeInfo::RawUntypedPtr
);
if !is_valid_arg_ty {
errors.push(CompileError::IntrinsicUnsupportedArgType {
name: kind.to_string(),
Expand Down Expand Up @@ -378,7 +380,7 @@ impl ty::TyIntrinsicFunctionKind {
type_arguments: vec![],
span,
};
let return_type = insert_type(TypeInfo::UnsignedInteger(IntegerBits::SixtyFour));
let return_type = insert_type(TypeInfo::RawUntypedPtr);
(intrinsic_function, return_type)
}
Intrinsic::StateLoadWord => {
Expand Down
1 change: 1 addition & 0 deletions sway-core/src/semantic_analysis/node_dependencies.rs
Original file line number Diff line number Diff line change
Expand Up @@ -751,6 +751,7 @@ fn type_info_name(type_info: &TypeInfo) -> String {
TypeInfo::Enum { .. } => "enum",
TypeInfo::Array(..) => "array",
TypeInfo::Storage { .. } => "contract storage",
TypeInfo::RawUntypedPtr => "raw untyped ptr",
}
.to_string()
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -899,6 +899,7 @@ fn type_name_to_type_info_opt(name: &Ident) -> Option<TypeInfo> {
"bool" => Some(TypeInfo::Boolean),
"unit" => Some(TypeInfo::Tuple(Vec::new())),
"b256" => Some(TypeInfo::B256),
"raw_ptr" => Some(TypeInfo::RawUntypedPtr),
"Self" | "self" => Some(TypeInfo::SelfType),
"Contract" => Some(TypeInfo::Contract),
_other => None,
Expand Down
1 change: 1 addition & 0 deletions sway-core/src/type_system/type_id.rs
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,7 @@ impl ReplaceSelfType for TypeId {
| TypeInfo::ContractCaller { .. }
| TypeInfo::B256
| TypeInfo::Numeric
| TypeInfo::RawUntypedPtr
| TypeInfo::Contract
| TypeInfo::ErrorRecovery => {}
}
Expand Down
17 changes: 17 additions & 0 deletions sway-core/src/type_system/type_info.rs
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,13 @@ pub enum TypeInfo {
Storage {
fields: Vec<ty::TyStructField>,
},
/// Raw untyped pointers.
/// These are represented in memory as u64 but are a different type since pointers only make
/// sense in the context they were created in. Users can obtain pointers via standard library
/// functions such `alloc` or `stack_ptr`. These functions are implemented using asm blocks
/// which can create pointers by (eg.) reading logically-pointer-valued registers, using the
/// gtf instruction, or manipulating u64s.
RawUntypedPtr,
}

// NOTE: Hash and PartialEq must uphold the invariant:
Expand Down Expand Up @@ -177,6 +184,9 @@ impl Hash for TypeInfo {
look_up_type_id(*elem_ty).hash(state);
count.hash(state);
}
TypeInfo::RawUntypedPtr => {
state.write_u8(18);
}
}
}
}
Expand Down Expand Up @@ -256,6 +266,7 @@ impl PartialEq for TypeInfo {
(TypeInfo::Storage { fields: l_fields }, TypeInfo::Storage { fields: r_fields }) => {
l_fields == r_fields
}
(TypeInfo::RawUntypedPtr, TypeInfo::RawUntypedPtr) => true,
_ => false,
}
}
Expand Down Expand Up @@ -325,6 +336,7 @@ impl fmt::Display for TypeInfo {
}
Array(elem_ty, count, _) => format!("[{}; {}]", elem_ty, count),
Storage { .. } => "contract storage".into(),
RawUntypedPtr => "raw untyped ptr".into(),
};
write!(f, "{}", s)
}
Expand Down Expand Up @@ -369,6 +381,7 @@ impl TypeInfo {
}
Array(elem_ty, count, _) => format!("[{}; {}]", elem_ty.json_abi_str(), count),
Storage { .. } => "contract storage".into(),
RawUntypedPtr => "raw untyped ptr".into(),
}
}
/// maps a type to a name that is used when constructing function selectors
Expand Down Expand Up @@ -528,6 +541,7 @@ impl TypeInfo {
};
format!("a[{};{}]", name, size)
}
RawUntypedPtr => "rawptr".to_string(),
_ => {
return err(
vec![],
Expand Down Expand Up @@ -679,6 +693,7 @@ impl TypeInfo {
| TypeInfo::SelfType
| TypeInfo::B256
| TypeInfo::Numeric
| TypeInfo::RawUntypedPtr
| TypeInfo::Contract
| TypeInfo::ErrorRecovery
| TypeInfo::Array(_, _, _)
Expand Down Expand Up @@ -707,6 +722,7 @@ impl TypeInfo {
| TypeInfo::UnknownGeneric { .. }
| TypeInfo::Numeric => ok((), warnings, errors),
TypeInfo::Unknown
| TypeInfo::RawUntypedPtr
| TypeInfo::ContractCaller { .. }
| TypeInfo::Custom { .. }
| TypeInfo::SelfType
Expand Down Expand Up @@ -818,6 +834,7 @@ impl TypeInfo {
| TypeInfo::ContractCaller { .. }
| TypeInfo::B256
| TypeInfo::Numeric
| TypeInfo::RawUntypedPtr
| TypeInfo::Contract
| TypeInfo::ErrorRecovery => {}
TypeInfo::Custom { .. } | TypeInfo::SelfType => {
Expand Down
1 change: 1 addition & 0 deletions sway-core/src/type_system/type_mapping.rs
Original file line number Diff line number Diff line change
Expand Up @@ -352,6 +352,7 @@ impl TypeMapping {
| TypeInfo::SelfType
| TypeInfo::B256
| TypeInfo::Numeric
| TypeInfo::RawUntypedPtr
| TypeInfo::Contract
| TypeInfo::ErrorRecovery => None,
}
Expand Down
2 changes: 2 additions & 0 deletions sway-core/src/type_system/unify.rs
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ pub(super) fn unify(
(B256, B256) => (vec![], vec![]),
(Numeric, Numeric) => (vec![], vec![]),
(Contract, Contract) => (vec![], vec![]),
(RawUntypedPtr, RawUntypedPtr) => (vec![], vec![]),
(Str(l), Str(r)) => unify::unify_strs(
received,
expected,
Expand Down Expand Up @@ -290,6 +291,7 @@ pub(super) fn unify_right(
(B256, B256) => (vec![], vec![]),
(Numeric, Numeric) => (vec![], vec![]),
(Contract, Contract) => (vec![], vec![]),
(RawUntypedPtr, RawUntypedPtr) => (vec![], vec![]),
(Str(l), Str(r)) => unify::unify_strs(received, expected, span, help_text, l, r, false),
(Tuple(rfs), Tuple(efs)) if rfs.len() == efs.len() => {
unify::unify_tuples(help_text, rfs, efs, curried)
Expand Down
3 changes: 3 additions & 0 deletions sway-error/src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -656,6 +656,8 @@ pub enum CompileError {
ConfigTimeConstantNotALiteral { span: Span },
#[error("ref mut parameter not allowed for main()")]
RefMutableNotAllowedInMain { param_name: Ident },
#[error("returning a `raw_ptr` from `main()` is not allowed")]
PointerReturnNotAllowedInMain { span: Span },
}

impl std::convert::From<TypeError> for CompileError {
Expand Down Expand Up @@ -832,6 +834,7 @@ impl Spanned for CompileError {
ConfigTimeConstantNotAConstDecl { span } => span.clone(),
ConfigTimeConstantNotALiteral { span } => span.clone(),
RefMutableNotAllowedInMain { param_name } => param_name.span(),
PointerReturnNotAllowedInMain { span } => span.clone(),
}
}
}
Expand Down
2 changes: 2 additions & 0 deletions sway-lib-core/src/lib.sw
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
library core;

dep num;
dep raw_ptr;
dep ops;
dep prelude;
6 changes: 6 additions & 0 deletions sway-lib-core/src/ops.sw
Original file line number Diff line number Diff line change
Expand Up @@ -213,6 +213,12 @@ impl Eq for b256 {
}
}

impl Eq for raw_ptr {
fn eq(self, other: Self) -> bool {
__eq(self, other)
}
}

pub trait Ord {
fn gt(self, other: Self) -> bool;
fn lt(self, other: Self) -> bool;
Expand Down
7 changes: 7 additions & 0 deletions sway-lib-core/src/prelude.sw
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
library prelude;

//! Defines the Sway core library prelude.
//! The prelude consists of implicitly available items,
//! for which `use` is not required.
use ::num::*;
use ::raw_ptr::*;
60 changes: 60 additions & 0 deletions sway-lib-core/src/raw_ptr.sw
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
library raw_ptr;

impl raw_ptr {
/// Returns `true` if the pointer is null.
pub fn is_null(self) -> bool {
let addr = asm(ptr: self) { ptr: u64 };
__eq(addr, 0)
}

/// Gets the address of the pointer.
pub fn addr(self) -> u64 {
asm(ptr: self) { ptr: u64 }
}

/// Calculates the offset from the pointer.
pub fn add(self, count: u64) -> raw_ptr {
let addr = asm(ptr: self) { ptr: u64 };
let addr = __add(addr, count);
asm(ptr: addr) { ptr: raw_ptr }
}

/// Calculates the offset from the pointer.
pub fn sub(self, count: u64) -> raw_ptr {
let addr = asm(ptr: self) { ptr: u64 };
let addr = __sub(addr, count);
asm(ptr: addr) { ptr: raw_ptr }
}

/// Reads the given type of value from the address.
pub fn read<T>(self) -> T {
if __is_reference_type::<T>() {
asm(ptr: self) { ptr: T }
} else {
asm(ptr: self, val) {
lw val ptr i0;
val: T
}
}
}

/// Copies `size` bytes from `self` to `dst`.
pub fn copy_to(self, dst: raw_ptr, count: u64) {
asm(dst: dst, src: self, count: count) {
mcp dst src count;
};
}

/// Writes the given value to the address.
pub fn write<T>(self, val: T) {
if __is_reference_type::<T>() {
asm(dst: self, src: val, count: __size_of_val(val)) {
mcp dst src count;
};
} else {
asm(ptr: self, val: val) {
sw ptr val i0;
};
}
}
}
4 changes: 1 addition & 3 deletions sway-lib-std/src/address.sw
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
library address;
//! A wrapper around the b256 type to help enhance type-safety.
use ::intrinsics::size_of_val;
use ::mem::{addr_of, eq};
/// The Address type, a struct wrappper around the inner `value`.
pub struct Address {
Expand All @@ -10,7 +8,7 @@ pub struct Address {

impl core::ops::Eq for Address {
fn eq(self, other: Self) -> bool {
eq(addr_of(self), addr_of(other), size_of_val(self))
self.value == other.value
}
}

Expand Down
10 changes: 4 additions & 6 deletions sway-lib-std/src/alloc.sw
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,6 @@
//! Inspired from: https://doc.rust-lang.org/std/alloc/index.html
library alloc;

use ::mem::copy;

/// Allocates zeroed memory on the heap
///
/// In FuelVM, the heap begins at `VM_MAX_RAM - 1` and grows downward.
Expand All @@ -23,22 +21,22 @@ use ::mem::copy;
///
/// See: https://github.com/FuelLabs/fuel-specs/blob/master/specs/vm/main.md#vm-initialization
/// See: https://github.com/FuelLabs/fuel-specs/blob/master/specs/vm/opcodes.md#aloc-allocate-memory
pub fn alloc(size: u64) -> u64 {
pub fn alloc(size: u64) -> raw_ptr {
asm(size: size, ptr) {
aloc size;
// `$hp` points to unallocated space and heap grows downward so
// our newly allocated space will be right after it
addi ptr hp i1;
ptr: u64
ptr: raw_ptr
}
}

/// Reallocates the given area of memory
pub fn realloc(ptr: u64, size: u64, new_size: u64) -> u64 {
pub fn realloc(ptr: raw_ptr, size: u64, new_size: u64) -> raw_ptr {
if new_size > size {
let new_ptr = alloc(new_size);
if size > 0 {
copy(ptr, new_ptr, size);
ptr.copy_to(new_ptr, size);
}
new_ptr
} else {
Expand Down
Loading

0 comments on commit 13ffbc3

Please sign in to comment.