After an evaluation, GNOME has moved from Bugzilla to GitLab. Learn more about GitLab.
No new issues can be reported in GNOME Bugzilla anymore.
To report an issue in a GNOME project, go to GNOME GitLab.
Do not go to GNOME Gitlab for: Bluefish, Doxygen, GnuCash, GStreamer, java-gnome, LDTP, NetworkManager, Tomboy.
Bug 792534 - Vala's pointer based generics system should not compile when used with an array of value types
Vala's pointer based generics system should not compile when used with an arr...
Status: RESOLVED OBSOLETE
Product: vala
Classification: Core
Component: Generics
0.36.x
Other Linux
: Normal normal
: ---
Assigned To: Vala maintainers
Vala maintainers
: 791556 (view as bug list)
Depends on:
Blocks:
 
 
Reported: 2018-01-15 14:49 UTC by fsb4000
Modified: 2018-05-22 15:58 UTC
See Also:
GNOME target: ---
GNOME version: ---



Description fsb4000 2018-01-15 14:49:19 UTC
Consider the code:
/* file test.vala */
void main() {
  int[] numbers = { 10, 30, 1, 8, -5, 7, 666, 12 };
  foreach (int a in numbers) { stdout.printf("%d ", a); }
  stdout.printf("\n");
  qsort_with_data<int>(numbers, sizeof(int), (a, b) => { return a == b ? 0 : a < b ? -1 : 1; });
  foreach (int a in numbers) { stdout.printf("%d ", a); }
  stdout.printf("\n");
}

If you will compile it with the command:
 valac -X -fsanitize=address -X -fsanitize=undefined -X -fsanitize=leak -X -fno-omit-frame-pointer test.vala

You will get the output:
 (linux mint, gcc-7, valac 0.36.8)

10 30 1 8 -5 7 666 12 
/usr/share/vala-0.36/vapi/glib-2.0.vapi:5714:11: runtime error: load of misaligned address 0x60300000efb4 for type 'const <unknown> *', which requires 8 byte alignment
0x60300000efb4: note: pointer points here
  0a 00 00 00 1e 00 00 00  01 00 00 00 08 00 00 00  fb ff ff ff 07 00 00 00  9a 02 00 00 0c 00 00 00
              ^ 
/usr/share/vala-0.36/vapi/glib-2.0.vapi:5714:11: runtime error: load of misaligned address 0x60300000efc4 for type 'const <unknown> *', which requires 8 byte alignment
0x60300000efc4: note: pointer points here
  fb ff ff ff 07 00 00 00  0c 00 00 00 9a 02 00 00  02 00 00 00 ff ff ff 02  20 00 00 00 01 00 00 7a
              ^ 
-5 1 7 8 10 12 30 666

or if you use FreeBSD, you will get:
 0 30 1 8 -5 7 666 12 
/root/MyProjects/test.vala.c:68:28: runtime error: load of misaligned address 0x603000000314 for type 'gconstpointer' (aka 'const void *'), which requires 8 byte alignment
0x603000000314: note: pointer points here
  0a 00 00 00 1e 00 00 00  01 00 00 00 08 00 00 00  fb ff ff ff 07 00 00 00  9a 02 00 00 0c 00 00 00
              ^ 
SUMMARY: AddressSanitizer: undefined-behavior /root/MyProjects/test.vala.c:68:28 in 
/root/MyProjects/test.vala.c:68:19: runtime error: load of misaligned address 0x603000000324 for type 'gconstpointer' (aka 'const void *'), which requires 8 byte alignment
0x603000000324: note: pointer points here
  fb ff ff ff 07 00 00 00  0c 00 00 00 9a 02 00 00  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00
              ^ 
SUMMARY: AddressSanitizer: undefined-behavior /root/MyProjects/test.vala.c:68:19 in 
-5 1 7 8 10 12 30 666

If you sort arrays with Posix.qsort, you will not receive sanitizer errors.
Comment 1 Al Thomas 2018-01-15 17:36:47 UTC
(In reply to fsb4000 from comment #0)
> Consider the code:
>   qsort_with_data<int>

I've not looked at your code in detail, but as soon as I saw int used with generics I wondered if the problem was Vala's pointer based generics. See https://bugzilla.gnome.org/show_bug.cgi?id=774713

Vala uses pointers for generics so value types are difficult. There are two schemes used by Vala to generate C code from a Vala type parameter (generic). The first adds additional fields. This code:

void main () {
    test<int> ();
}

void test<T> () {
}

compiled with `valac --ccode generics.vala` will produce a function defined as:
void
test (GType t_type,
      GBoxedCopyFunc t_dup_func,
      GDestroyNotify t_destroy_func)
{
}
and be called from the generated C code main () as test (G_TYPE_INT, NULL, NULL);

In this scheme Vala is adding parameters to the function signature to communicate details of the type. In this example only the GType information is passed, the copy and destroy functions are NULL because an int is a value type.

What you can do is "box" the int with a ? to make it a reference type. This code:

void main () {
    test<int?> ();
}

void test<T> () {
}

will show the call to test () in main as:

test (G_TYPE_INT, (GBoxedCopyFunc) _int_dup, (GDestroyNotify) g_free);

Although qsort_with_data () is bound with the second scheme detailed next, you should try "boxing" the int in your ASAN tests. In general I'm starting to think it would be a good idea for Vala to automatically box value types. This means it would just work, but with a small additional memory burden. If the programmer wanted to avoid that then they shouldn't use generics.

The second scheme in Vala is simple generics. This is mostly used for binding to C libraries. This code:

void main () {
    test<int> ();
}

[CCode (simple_generics = true)]
void test<T> () {
}

will show the call in the generated C code main () as simply test (); No type information is passed at the C level, but is still available at the Vala level of the compilation process.

Note that this specific example shows a bug in Vala code generation. The simple_generics attribute is ignored in the generated function declaration, but acknowledged in the function call. So invalid C is generated.

The binding for g_qsort_with_data () in glib-2.0.vapi is:

	[CCode (simple_generics = true, cname = "g_qsort_with_data")]
	private static void _qsort_with_data<T> (T[] elems, size_t size, [CCode (type = "GCompareDataFunc")] GLib.CompareDataFunc<T> compare_func);

	[CCode (cname = "_vala_g_qsort_with_data")]
	public static void qsort_with_data<T> (T[] elems, size_t size, [CCode (type = "GCompareDataFunc")] GLib.CompareDataFunc<T> compare_func) {
		_qsort_with_data<T*> (elems, size, (a, b) => {
				return compare_func (*a, *b);
			});
	}

I'm guessing the T* means that the binding can only be used with arrays of pointers. See https://bugzilla.gnome.org/show_bug.cgi?id=640786 for details on the current binding.

Given generics in Vala are pointer based there is a conflict between the use of generics and arrays of values types. Your code with qsort_with_data () should probably produce an error from the Vala compiler stating generics are incompatible with arrays of value types. An alternative would be not to use generics in this binding, so the data type for the array is given as the size, but that would also mean changing the binding for CompareDataFunc.

> If you sort arrays with Posix.qsort, you will not receive sanitizer errors.

The Posix.qsort binding doesn't use generics.

I hope that gives you some insight if you wish to continue looking in to this problem.
Comment 2 Al Thomas 2018-01-15 18:33:48 UTC
*** Bug 791556 has been marked as a duplicate of this bug. ***
Comment 3 fsb4000 2018-01-15 19:11:08 UTC
Al Thomas, thank you for your answer.
Yes, it gave me some insight.
I tested with int? and it works without sanitizer errors.
I do not have the knowledge to decide what is the best: automatically box value types or to produce an error, but for now I have one small proposal for this description:
https://valadoc.org/glib-2.0/GLib.qsort_with_data.html
add a text, that T should be pointer type, not value type.

Thank you again for a fast response and the detailed explanation!
Comment 4 GNOME Infrastructure Team 2018-05-22 15:58:56 UTC
-- GitLab Migration Automatic Message --

This bug has been migrated to GNOME's GitLab instance and has been closed from further activity.

You can subscribe and participate further through the new bug through this link to our GitLab instance: https://gitlab.gnome.org/GNOME/vala/issues/612.