Repository: numpy/numpy
Pull request: BUG: longdouble with elsize 12 is never uint alignable
Fix #12607 

It seems we hit an alignment assert on 32bit linux with longdouble dtype (which has elsize 12). Fix it in two stages (the first commit only extends CI to test asserts):

1. Add `-UNDEBUG` to the `CFLAGS` so that the assert statements are checked (debug mode)
2. Find and fix the underlying alignment issue

Problem statement:
1.6.0rc1 3.7 debug: test failure: test_multiarray.py::TestAlignment::test_various_alignments Aborted
Hello,
when running the test suite on i386 wit python3.7 in the debug flavor we get this abort:

```
-- running tests for 3.7 debug --
NumPy version 1.16.0rc1
NumPy relaxed strides checking option: True
============================= test session starts ==============================
platform linux -- Python 3.7.2rc1, pytest-3.10.1, py-1.7.0, pluggy-0.8.0 -- /usr/bin/python3.7-dbg
cachedir: .pytest_cache
rootdir: /<<PKGBUILDDIR>>, inifile: pytest.ini
collecting ... collected 7336 items / 167 deselected
...
tmp/usr/lib/python3/dist-packages/numpy/core/tests/test_multiarray.py::TestAlignment::test_various_alignments Aborted
```

could you have a look?

thanks!

Hints from issue discussion:
@ahaldane I don't know if it is your problem, but it is your test :)
@sandrotosi What hardware? What does aborted mean?

EDIT: Could you instrument the test so that we have a better idea of where it aborts?
the full log of the failure is at: https://buildd.debian.org/status/fetch.php?pkg=python-numpy&arch=i386&ver=1%3A1.16.0~rc1-1&stamp=1545690613&raw=0

the machine where it was built was: https://db.debian.org/machines.cgi?host=x86-ubc-01 (i dont have access to it to run commands, but i suspect it may be an actual amd64 hardware running i386 chroots for packages building purposes)

is there any switch/option you want me to pass to the tests? is the abort unreproducible on your end?
Just printing out the parameters would help locate what is causing the problem. I assume abort is some sort of illegal access? The test is doing a fair amount fooling with locations and it is passing on our normal tests, so the difference is the debug option and I don't know exactly what it is failing on here. Is there a way to get more information on the cause of the abort?
It could even just be a failed assert? Although certainly in one to do with alignment.
i'll have a look if i can setup an environment to replicate it on my hand, it may take some days.

`Aborted` means the interpreter was doing something so wrong that it got terminated, so likely not a simple failed assert.
Does it replicate with other Python versions? I'm thinking it might also be a Python bug.
Was suprising to me as well, but I just tried and if I add a random `assert(0)` in the code, I do get `Aborted (core dumped)`.
From the log file, the crash is in `numpy/core/tests/test_multiarray.py::TestAlignment::test_various_alignments` and ends with `Aborted` so probably an assert somewhere in that newish code. I cannot reproduce on a 32 bit chroot of `xenial` running on a 64 bit host.

<details>

Python, gcc

```
Python 2.7.11+ (default, Apr 17 2016, 14:00:29) 
[GCC 5.3.1 20160413] on linux2

$gcc --version
gcc (Ubuntu 5.3.1-14ubuntu2) 5.3.1 20160413
Copyright (C) 2015 Free Software Foundation, Inc.
```
Reproduces on python3.5-dbg in the chroot, now trying to run gdb

Commit message: Merge pull request #1 from ahaldane/mattip_align-longdouble

BUG: non-uint-aligned arrays were counted as uint-aligned

Patch to review:
diff --git a/azure-pipelines.yml b/azure-pipelines.yml
index b21a7fc7af35..6e7975039a55 100644
--- a/azure-pipelines.yml
+++ b/azure-pipelines.yml
@@ -6,7 +6,7 @@ trigger:
       - master
       - maintenance/*
 jobs:
-- job: Linux_Python_36_32bit_full
+- job: Linux_Python_36_32bit_full_with_asserts
   pool:
     vmIMage: 'ubuntu-16.04'
   steps:
@@ -26,7 +26,8 @@ jobs:
            cp ./usr/local/include/* /usr/include && \
            cd ../numpy && \
            NUMPY_EXPERIMENTAL_ARRAY_FUNCTION=1 \
-           F77=gfortran-5 F90=gfortran-5 python3 runtests.py --mode=full -- -rsx --junitxml=junit/test-results.xml"
+           F77=gfortran-5 F90=gfortran-5 \
+           CFLAGS=-UNDEBUG python3 runtests.py --mode=full -- -rsx --junitxml=junit/test-results.xml"
     displayName: 'Run 32-bit Ubuntu Docker Build / Tests'
   - task: PublishTestResults@2
     inputs:
diff --git a/numpy/core/src/common/array_assign.c b/numpy/core/src/common/array_assign.c
index ac3fdbef772f..8b76515ec93d 100644
--- a/numpy/core/src/common/array_assign.c
+++ b/numpy/core/src/common/array_assign.c
@@ -125,9 +125,13 @@ raw_array_is_aligned(int ndim, npy_intp *shape,
 
         return npy_is_aligned((void *)align_check, alignment);
     }
-    else {
+    else if (alignment == 1) {
         return 1;
     }
+    else {
+        /* always return false for alignment == 0, which means unaligned */
+        return 0;
+    }
 }
 
 NPY_NO_EXPORT int
diff --git a/numpy/core/src/common/array_assign.h b/numpy/core/src/common/array_assign.h
index 07438c5e8dba..23efd85205a0 100644
--- a/numpy/core/src/common/array_assign.h
+++ b/numpy/core/src/common/array_assign.h
@@ -88,7 +88,8 @@ broadcast_strides(int ndim, npy_intp *shape,
 /*
  * Checks whether a data pointer + set of strides refers to a raw
  * array whose elements are all aligned to a given alignment.
- * alignment should be a power of two.
+ * alignment should be a power of two, or may be the sentinel value 0 to mean
+ * unaligned, in which case false is always returned.
  */
 NPY_NO_EXPORT int
 raw_array_is_aligned(int ndim, npy_intp *shape,


Code review comments:
diff_hunk: @@ -88,7 +88,8 @@ broadcast_strides(int ndim, npy_intp *shape,
 /*
  * Checks whether a data pointer + set of strides refers to a raw
  * array whose elements are all aligned to a given alignment.
- * alignment should be a power of two.
+ * alignment should be a power of two, or may be the sentinel value 0 to mean
+ * unaligned, in which case false is always returned.
path: numpy/core/src/common/array_assign.h
text: @user1:
This comment confuses me - I read this as saying that `is_aligned(..., alignment = 0)` means `is_unaligned(...)`

@user2:
If I am not misreading you, that is what the comment is meant to say. If you pass an alignment of 0, the function always returns "not aligned". Do you mean I should say "not aligned" instead of "unaligned"?

@user1:
Right. the return value means "not aligned", but the meaning of the `alignment` argument is closer to "unalignable"

@author:
fixing