The problem appears to be triggered by two locations in the front-end
where non-POINTER_SIZE pointers aren't handled right now.

1. An assertion in strip_typedefs is triggered because the alignment
of the types don't match. This in turn is caused by creating the new
type with build_pointer_type instead of taking the type of the
original pointer into account.

2. An assertion in cp_convert_to_pointer is triggered which expects
the target type to always have POINTER_SIZE.

Ok for mainline?

gcc/cp/ChangeLog:

        PR c++/100281
        * cvt.c (cp_convert_to_pointer): Use the size of the target
        pointer type.
        * tree.c (strip_typedefs): Use build_pointer_type_for_mode for
        non-POINTER_SIZE pointers.

gcc/testsuite/ChangeLog:

        PR c++/100281
        * g++.target/s390/pr100281.C: New test.
---
 gcc/cp/cvt.c                             |  2 +-
 gcc/cp/tree.c                            |  5 ++++-
 gcc/testsuite/g++.target/s390/pr100281.C | 10 ++++++++++
 3 files changed, 15 insertions(+), 2 deletions(-)
 create mode 100644 gcc/testsuite/g++.target/s390/pr100281.C

diff --git a/gcc/cp/cvt.c b/gcc/cp/cvt.c
index f1687e804d1..7fa6e8df52b 100644
--- a/gcc/cp/cvt.c
+++ b/gcc/cp/cvt.c
@@ -232,7 +232,7 @@ cp_convert_to_pointer (tree type, tree expr, bool dofold,
     {
       if (TYPE_PRECISION (intype) == POINTER_SIZE)
        return build1 (CONVERT_EXPR, type, expr);
-      expr = cp_convert (c_common_type_for_size (POINTER_SIZE, 0), expr,
+      expr = cp_convert (c_common_type_for_size (TYPE_PRECISION (type), 0), 
expr,
                         complain);
       /* Modes may be different but sizes should be the same.  There
         is supposed to be some integral type that is the same width
diff --git a/gcc/cp/tree.c b/gcc/cp/tree.c
index a8bfd5fc053..6f6b732c9c9 100644
--- a/gcc/cp/tree.c
+++ b/gcc/cp/tree.c
@@ -1556,7 +1556,10 @@ strip_typedefs (tree t, bool *remove_attributes, 
unsigned int flags)
     {
     case POINTER_TYPE:
       type = strip_typedefs (TREE_TYPE (t), remove_attributes, flags);
-      result = build_pointer_type (type);
+      if (TYPE_PRECISION (t) == POINTER_SIZE)
+       result = build_pointer_type (type);
+      else
+       result = build_pointer_type_for_mode (type, TYPE_MODE (t), false);
       break;
     case REFERENCE_TYPE:
       type = strip_typedefs (TREE_TYPE (t), remove_attributes, flags);
diff --git a/gcc/testsuite/g++.target/s390/pr100281.C 
b/gcc/testsuite/g++.target/s390/pr100281.C
new file mode 100644
index 00000000000..f45798c3879
--- /dev/null
+++ b/gcc/testsuite/g++.target/s390/pr100281.C
@@ -0,0 +1,10 @@
+// PR C++/100281
+// { dg-do compile }
+
+typedef void * __attribute__((mode (SI))) __ptr32_t;
+
+void foo(){
+  unsigned int b = 100;
+  __ptr32_t a;
+  a = b; /* { dg-error "invalid conversion from 'unsigned int' to 
'__ptr32_t'.*" } */
+}
-- 
2.30.2

Reply via email to