diff --git a/compiler/src/type_check/expressions.rs b/compiler/src/type_check/expressions.rs index 97184c505..6796ca4f1 100644 --- a/compiler/src/type_check/expressions.rs +++ b/compiler/src/type_check/expressions.rs @@ -3849,7 +3849,8 @@ impl<'a> CheckMethodBody<'a> { ); } - let require_send = class.kind(self.db()).is_async(); + let kind = class.kind(self.db()); + let require_send = kind.is_async(); let ins = ClassInstance::empty(self.db_mut(), class); let mut assigned = HashSet::new(); let mut fields = Vec::new(); @@ -3984,17 +3985,27 @@ impl<'a> CheckMethodBody<'a> { fields.push((field, expected)); } - for field in class.field_names(self.db()) { - if assigned.contains(&field) { - continue; - } + // For extern classes we allow either all fields to be specified, or all + // fields to be left out. The latter is useful when dealing with C + // structures that start on the stack as uninitialized data and are + // initialized using a dedicated function. + // + // If an extern class has one or more fields specifid, then we require + // _all_ fields to be specified, as leaving out fields in this case is + // likely the result of a mistake. + if !kind.is_extern() || !assigned.is_empty() { + for field in class.field_names(self.db()) { + if assigned.contains(&field) { + continue; + } - self.state.diagnostics.error( - DiagnosticId::MissingField, - format!("the field '{}' must be assigned a value", field), - self.file(), - node.location.clone(), - ); + self.state.diagnostics.error( + DiagnosticId::MissingField, + format!("the field '{}' must be assigned a value", field), + self.file(), + node.location.clone(), + ); + } } let resolved_type = TypeRef::Owned(TypeId::ClassInstance(ins)); diff --git a/docs/source/getting-started/ffi.md b/docs/source/getting-started/ffi.md index 1b261829f..6cbefd65e 100644 --- a/docs/source/getting-started/ffi.md +++ b/docs/source/getting-started/ffi.md @@ -309,6 +309,25 @@ class async Main { } ``` +When creating an instance of a struct we can leave out all the fields. This +reserves the necessary stack space but doesn't initialize it. This is useful +when dealing with large structs that are initialized by a C function: + +```inko +class extern Timespec { + let @tv_sec: Int64 + let @tv_nsec: Int64 +} + +class async Main { + fn async main { + let spec = Timespec() + + hypothetical_initialization_function_that_sets_the_fields(mut spec) + } +} +``` + Structures are allocated on the stack and are value types, meaning a move results in a copy (unless this is optimised away). Reading and writing of structure fields uses the same syntax as regular Inko classes: diff --git a/std/fixtures/diagnostics/class_extern_without_fields.inko b/std/fixtures/diagnostics/class_extern_without_fields.inko new file mode 100644 index 000000000..e04763ed8 --- /dev/null +++ b/std/fixtures/diagnostics/class_extern_without_fields.inko @@ -0,0 +1,19 @@ +class extern Foo { + let @a: Int32 + let @b: Int32 + let @c: Int32 +} + +fn example1 { + let foo = Foo() +} + +fn example2 { + let foo = Foo(a: 0 as Int32, b: 1 as Int32, c: 2 as Int32) +} + +fn example3 { + let foo = Foo(a: 0 as Int32, b: 1 as Int32) +} + +# class_extern_without_fields.inko:16:13 error(missing-field): the field 'c' must be assigned a value diff --git a/std/src/std/sys/freebsd/fs.inko b/std/src/std/sys/freebsd/fs.inko index 6c8adc65a..f680a9bbf 100644 --- a/std/src/std/sys/freebsd/fs.inko +++ b/std/src/std/sys/freebsd/fs.inko @@ -28,36 +28,7 @@ fn stat_to_metadata(buf: Pointer[libc.StatBuf]) -> Metadata { } fn file_metadata(fd: Int32) -> Result[Metadata, Error] { - let buf = libc.StatBuf( - st_dev: 0 as UInt64, - st_ino: 0 as UInt64, - st_nlink: 0 as UInt64, - st_mode: 0 as UInt16, - __pad0: 0 as Int16, - st_uid: 0 as UInt32, - st_gid: 0 as UInt32, - __pad1: 0 as Int32, - st_rdev: 0 as UInt64, - st_atim: libc.Timespec(tv_sec: 0 as Int64, tv_nsec: 0 as Int64), - st_mtim: libc.Timespec(tv_sec: 0 as Int64, tv_nsec: 0 as Int64), - st_ctim: libc.Timespec(tv_sec: 0 as Int64, tv_nsec: 0 as Int64), - st_birthtim: libc.Timespec(tv_sec: 0 as Int64, tv_nsec: 0 as Int64), - st_size: 0 as Int64, - st_blocks: 0 as Int64, - st_blksize: 0 as Int32, - st_flags: 0 as UInt32, - st_gen: 0 as UInt64, - st_spare0: 0 as Int64, - st_spare1: 0 as Int64, - st_spare2: 0 as Int64, - st_spare3: 0 as Int64, - st_spare4: 0 as Int64, - st_spare5: 0 as Int64, - st_spare6: 0 as Int64, - st_spare7: 0 as Int64, - st_spare8: 0 as Int64, - st_spare9: 0 as Int64, - ) + let buf = libc.StatBuf() start_blocking @@ -70,36 +41,7 @@ fn file_metadata(fd: Int32) -> Result[Metadata, Error] { } fn path_metadata(path: String) -> Result[Metadata, Error] { - let buf = libc.StatBuf( - st_dev: 0 as UInt64, - st_ino: 0 as UInt64, - st_nlink: 0 as UInt64, - st_mode: 0 as UInt16, - __pad0: 0 as Int16, - st_uid: 0 as UInt32, - st_gid: 0 as UInt32, - __pad1: 0 as Int32, - st_rdev: 0 as UInt64, - st_atim: libc.Timespec(tv_sec: 0 as Int64, tv_nsec: 0 as Int64), - st_mtim: libc.Timespec(tv_sec: 0 as Int64, tv_nsec: 0 as Int64), - st_ctim: libc.Timespec(tv_sec: 0 as Int64, tv_nsec: 0 as Int64), - st_birthtim: libc.Timespec(tv_sec: 0 as Int64, tv_nsec: 0 as Int64), - st_size: 0 as Int64, - st_blocks: 0 as Int64, - st_blksize: 0 as Int32, - st_flags: 0 as UInt32, - st_gen: 0 as UInt64, - st_spare0: 0 as Int64, - st_spare1: 0 as Int64, - st_spare2: 0 as Int64, - st_spare3: 0 as Int64, - st_spare4: 0 as Int64, - st_spare5: 0 as Int64, - st_spare6: 0 as Int64, - st_spare7: 0 as Int64, - st_spare8: 0 as Int64, - st_spare9: 0 as Int64, - ) + let buf = libc.StatBuf() start_blocking diff --git a/std/src/std/sys/linux/fs.inko b/std/src/std/sys/linux/fs.inko index ae2e541cb..e23d4aaca 100644 --- a/std/src/std/sys/linux/fs.inko +++ b/std/src/std/sys/linux/fs.inko @@ -12,59 +12,7 @@ fn statx_time(time: Pointer[libc.StatxTimestamp]) -> Time { } fn statx(fd: Int32, path: String) -> Result[Metadata, Error] { - let buf = libc.StatxBuf( - stx_mask: 0 as UInt32, - stx_blksize: 0 as UInt32, - stx_attributes: 0 as UInt64, - stx_nlink: 0 as UInt32, - stx_uid: 0 as UInt32, - stx_gid: 0 as UInt32, - stx_mode: 0 as UInt16, - __pad0: 0 as UInt16, - stx_ino: 0 as UInt64, - stx_size: 0 as UInt64, - stx_blocks: 0 as UInt64, - stx_attributes_mask: 0 as UInt64, - stx_atime: libc.StatxTimestamp( - tv_sec: 0 as Int64, - tv_nsec: 0 as UInt32, - __pad0: 0 as Int32, - ), - stx_btime: libc.StatxTimestamp( - tv_sec: 0 as Int64, - tv_nsec: 0 as UInt32, - __pad0: 0 as Int32, - ), - stx_ctime: libc.StatxTimestamp( - tv_sec: 0 as Int64, - tv_nsec: 0 as UInt32, - __pad0: 0 as Int32, - ), - stx_mtime: libc.StatxTimestamp( - tv_sec: 0 as Int64, - tv_nsec: 0 as UInt32, - __pad0: 0 as Int32, - ), - stx_rdev_major: 0 as UInt32, - stx_rdev_minor: 0 as UInt32, - stx_dev_major: 0 as UInt32, - stx_dev_minor: 0 as UInt32, - stx_mnt_id: 0 as UInt64, - stx_dio_mem_align: 0 as UInt32, - stx_dio_offset_align: 0 as UInt32, - __pad1: 0 as UInt64, - __pad2: 0 as UInt64, - __pad3: 0 as UInt64, - __pad4: 0 as UInt64, - __pad5: 0 as UInt64, - __pad6: 0 as UInt64, - __pad7: 0 as UInt64, - __pad8: 0 as UInt64, - __pad9: 0 as UInt64, - __pad10: 0 as UInt64, - __pad11: 0 as UInt64, - __pad12: 0 as UInt64, - ) + let buf = libc.StatxBuf() start_blocking diff --git a/std/src/std/sys/mac/fs.inko b/std/src/std/sys/mac/fs.inko index 8f49714ce..83f6fcef5 100644 --- a/std/src/std/sys/mac/fs.inko +++ b/std/src/std/sys/mac/fs.inko @@ -29,31 +29,7 @@ fn stat_to_metadata(buf: Pointer[libc.StatBuf]) -> Metadata { } fn file_metadata(fd: Int32) -> Result[Metadata, Error] { - let buf = libc.StatBuf( - st_dev: 0 as Int32, - st_mode: 0 as UInt16, - st_nlink: 0 as UInt16, - st_ino: 0 as UInt64, - st_uid: 0 as UInt32, - st_gid: 0 as UInt32, - st_rdev: 0 as Int32, - st_atime: 0 as Int64, - st_atime_nsec: 0 as Int64, - st_mtime: 0 as Int64, - st_mtime_nsec: 0 as Int64, - st_ctime: 0 as Int64, - st_ctime_nsec: 0 as Int64, - st_birthtime: 0 as Int64, - st_birthtime_nsec: 0 as Int64, - st_size: 0 as Int64, - st_blocks: 0 as Int64, - st_blksize: 0 as Int32, - st_flags: 0 as UInt32, - st_gen: 0 as UInt32, - st_lspare: 0 as Int32, - st_qspare0: 0 as Int64, - st_qspare1: 0 as Int64, - ) + let buf = libc.StatBuf() start_blocking @@ -66,31 +42,7 @@ fn file_metadata(fd: Int32) -> Result[Metadata, Error] { } fn path_metadata(path: String) -> Result[Metadata, Error] { - let buf = libc.StatBuf( - st_dev: 0 as Int32, - st_mode: 0 as UInt16, - st_nlink: 0 as UInt16, - st_ino: 0 as UInt64, - st_uid: 0 as UInt32, - st_gid: 0 as UInt32, - st_rdev: 0 as Int32, - st_atime: 0 as Int64, - st_atime_nsec: 0 as Int64, - st_mtime: 0 as Int64, - st_mtime_nsec: 0 as Int64, - st_ctime: 0 as Int64, - st_ctime_nsec: 0 as Int64, - st_birthtime: 0 as Int64, - st_birthtime_nsec: 0 as Int64, - st_size: 0 as Int64, - st_blocks: 0 as Int64, - st_blksize: 0 as Int32, - st_flags: 0 as UInt32, - st_gen: 0 as UInt32, - st_lspare: 0 as Int32, - st_qspare0: 0 as Int64, - st_qspare1: 0 as Int64, - ) + let buf = libc.StatBuf() start_blocking