[clang] Fix evaluation of the unsigned enumeration values, #108766 (PR #108769)
https://github.com/fursov created https://github.com/llvm/llvm-project/pull/108769 If the type of an enumeration is not native (e.g. uint8_t) but is unsigned then evaluation of the value might get wrong: for values bigger than half of type range the return value will be negative. This happens because for non-native enum types the TypeKind is ELABORATED. To get the real integer type, the conversion to canonical type is required before the enum_value() function can decide about type being signed or unsigned. This is fix for ticket #108766: https://github.com/llvm/llvm-project/issues/108766 >From 800010fabe3160e701ff58f7789dd9e01c80451f Mon Sep 17 00:00:00 2001 From: Dmitry Fursov Date: Sun, 15 Sep 2024 18:14:48 +0300 Subject: [PATCH] Fix evaluation of the unsigned enumeration values If the type of an enumeration is not native (e.g. uint8_t) but is unsigned then evaluation of the value might get wrong: for values bigger than half of type range the return value will be negative. This happens because for non-native enum types the TypeKind is ELABORATED. To get the real integer type, the conversion to canonical type is required before the enum_value() function can decide about type being signed or unsigned. --- clang/bindings/python/clang/cindex.py | 2 ++ .../python/tests/cindex/test_cursor.py| 19 +++ 2 files changed, 21 insertions(+) diff --git a/clang/bindings/python/clang/cindex.py b/clang/bindings/python/clang/cindex.py index 4da99e899e7f7c..c582c82ce50789 100644 --- a/clang/bindings/python/clang/cindex.py +++ b/clang/bindings/python/clang/cindex.py @@ -1952,6 +1952,8 @@ def enum_value(self): underlying_type = self.type if underlying_type.kind == TypeKind.ENUM: underlying_type = underlying_type.get_declaration().enum_type +if underlying_type.kind == TypeKind.ELABORATED: +underlying_type = underlying_type.get_canonical() if underlying_type.kind in ( TypeKind.CHAR_U, TypeKind.UCHAR, diff --git a/clang/bindings/python/tests/cindex/test_cursor.py b/clang/bindings/python/tests/cindex/test_cursor.py index 7476947bde2ea6..61a7bc1414f235 100644 --- a/clang/bindings/python/tests/cindex/test_cursor.py +++ b/clang/bindings/python/tests/cindex/test_cursor.py @@ -570,6 +570,25 @@ def test_enum_values_cpp(self): self.assertEqual(ham.kind, CursorKind.ENUM_CONSTANT_DECL) self.assertEqual(ham.enum_value, 0x100) +def test_enum_values_on_elaborated_type(self): +tu = get_tu( +"using myUType = unsigned char; enum TEST : myUType { SPAM = 1, HAM = 0xff;", lang="cpp" +) +enum = get_cursor(tu, "TEST") +self.assertIsNotNone(enum) + +self.assertEqual(enum.kind, CursorKind.ENUM_DECL) + +enum_constants = list(enum.get_children()) +self.assertEqual(len(enum_constants), 2) + +spam, ham = enum_constants + +self.assertEqual(spam.kind, CursorKind.ENUM_CONSTANT_DECL) +self.assertEqual(spam.enum_value, 1) +self.assertEqual(ham.kind, CursorKind.ENUM_CONSTANT_DECL) +self.assertEqual(ham.enum_value, 255) + def test_annotation_attribute(self): tu = get_tu( 'int foo (void) __attribute__ ((annotate("here be annotation attribute")));' ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] Fix evaluation of the unsigned enumeration values, #108766 (PR #108769)
fursov wrote: @DeinAlptraum, thank you for the review. The findings are fixed now. Could you say if the fix to be backported to other relelases, like 18.x or 19rc? If yes, how this is done? https://github.com/llvm/llvm-project/pull/108769 ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] [libclang/python] Fix evaluation of the unsigned enumeration values, #108766 (PR #108769)
@@ -1952,6 +1952,8 @@ def enum_value(self): underlying_type = self.type if underlying_type.kind == TypeKind.ENUM: underlying_type = underlying_type.get_declaration().enum_type +if underlying_type.kind == TypeKind.ELABORATED: fursov wrote: I ran the check-clang-python tests using clang 15 - it also does not resolve the enum type to the integer types. But since there was no elaborated type yet, it returns TypeKind.TYPEDEF instead (that matches the godbolt example from above - the TUintType in clang15 - typedef, in clang16 - elaborated and only then typedef). The test like in patch above (test_enum_values_on_elaborated_type) fails same way as on "main" branch: ``` self.assertEqual(ham.enum_value, 255) AssertionError: -1 != 255 ``` https://github.com/llvm/llvm-project/pull/108769 ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] [libclang/python] Fix evaluation of the unsigned enumeration values, #108766 (PR #108769)
https://github.com/fursov edited https://github.com/llvm/llvm-project/pull/108769 ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] [libclang/python] Fix evaluation of the unsigned enumeration values, #108766 (PR #108769)
@@ -1952,6 +1952,8 @@ def enum_value(self): underlying_type = self.type if underlying_type.kind == TypeKind.ENUM: underlying_type = underlying_type.get_declaration().enum_type +if underlying_type.kind == TypeKind.ELABORATED: fursov wrote: My educated guess: the call chain looks like this: enum_type (cindex.py) -> clang_getEnumDeclIntegerType (clang/tools/libclang/CXType.cpp) -> getIntegerType (clang/include/clang/AST/Decl.h) Let's use this example: `using TUintType = uint8_t; enum class ETUintType : TUintType { A = 0xff, B = 0x20 }; ` >From what I can see, e.g. from ast dumper (clang/lib/AST/TextNodeDumper.cpp, >TextNodeDumper::VisitEnumDecl), first it dumps scoped class, then name >(ETUintType), then it uses function dumpType() to print the type: first, the >one that has been provided by getIntegerType(), but then it is desugared. EnumDecl 0x5580b1f62490 line:5:12 class ETUintType 'TUintType':'unsigned char' This leads me to the conclusion, that getIntegerType() on the AST parser on purpose returns whatever type that enum is defined against without resolving to the target canonical type. But I would like to hear the opinion from experts. https://github.com/llvm/llvm-project/pull/108769 ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] [libclang/python] Fix evaluation of the unsigned enumeration values, #108766 (PR #108769)
@@ -1952,6 +1952,8 @@ def enum_value(self): underlying_type = self.type if underlying_type.kind == TypeKind.ENUM: underlying_type = underlying_type.get_declaration().enum_type +if underlying_type.kind == TypeKind.ELABORATED: fursov wrote: @Endilll , do you think the getIntegerType function on the EnumDecl class should return the true integer type to which any sugared type resolves? https://github.com/llvm/llvm-project/pull/108769 ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] [libclang/python] Fix evaluation of the unsigned enumeration values, #108766 (PR #108769)
fursov wrote: Sorry, I'm not familiar with the process (this is my first PR on github) - how the change gets now to the repo? Someone from llvm-project should push it? https://github.com/llvm/llvm-project/pull/108769 ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] [libclang/python] Fix evaluation of the unsigned enumeration values, #108766 (PR #108769)
https://github.com/fursov updated https://github.com/llvm/llvm-project/pull/108769 >From 4d9f2e2e9ac57b4279a4aafc078a3b4fb3ff646f Mon Sep 17 00:00:00 2001 From: Dmitry Fursov Date: Sun, 15 Sep 2024 18:14:48 +0300 Subject: [PATCH 1/2] Fix evaluation of the unsigned enumeration values If the type of an enumeration is not native (e.g. uint8_t) but is unsigned then evaluation of the value might get wrong: for values bigger than half of type range the return value will be negative. This happens because for non-native enum types the TypeKind is ELABORATED. To get the real integer type, the conversion to canonical type is required before the enum_value() function can decide about type being signed or unsigned. --- clang/bindings/python/clang/cindex.py | 2 ++ .../python/tests/cindex/test_cursor.py| 20 +++ 2 files changed, 22 insertions(+) diff --git a/clang/bindings/python/clang/cindex.py b/clang/bindings/python/clang/cindex.py index 4da99e899e7f7c..c582c82ce50789 100644 --- a/clang/bindings/python/clang/cindex.py +++ b/clang/bindings/python/clang/cindex.py @@ -1952,6 +1952,8 @@ def enum_value(self): underlying_type = self.type if underlying_type.kind == TypeKind.ENUM: underlying_type = underlying_type.get_declaration().enum_type +if underlying_type.kind == TypeKind.ELABORATED: +underlying_type = underlying_type.get_canonical() if underlying_type.kind in ( TypeKind.CHAR_U, TypeKind.UCHAR, diff --git a/clang/bindings/python/tests/cindex/test_cursor.py b/clang/bindings/python/tests/cindex/test_cursor.py index 7476947bde2ea6..927e35993a1c37 100644 --- a/clang/bindings/python/tests/cindex/test_cursor.py +++ b/clang/bindings/python/tests/cindex/test_cursor.py @@ -570,6 +570,26 @@ def test_enum_values_cpp(self): self.assertEqual(ham.kind, CursorKind.ENUM_CONSTANT_DECL) self.assertEqual(ham.enum_value, 0x100) +def test_enum_values_on_elaborated_type(self): +tu = get_tu( +"using myUType = unsigned char; enum TEST : myUType { SPAM = 1, HAM = 0xff; }", +lang="cpp", +) +enum = get_cursor(tu, "TEST") +self.assertIsNotNone(enum) + +self.assertEqual(enum.kind, CursorKind.ENUM_DECL) + +enum_constants = list(enum.get_children()) +self.assertEqual(len(enum_constants), 2) + +spam, ham = enum_constants + +self.assertEqual(spam.kind, CursorKind.ENUM_CONSTANT_DECL) +self.assertEqual(spam.enum_value, 1) +self.assertEqual(ham.kind, CursorKind.ENUM_CONSTANT_DECL) +self.assertEqual(ham.enum_value, 255) + def test_annotation_attribute(self): tu = get_tu( 'int foo (void) __attribute__ ((annotate("here be annotation attribute")));' >From 683d2aaac80c715eea298eb1f65f05959fd320f6 Mon Sep 17 00:00:00 2001 From: Dmitry Fursov Date: Wed, 18 Sep 2024 13:57:56 +0300 Subject: [PATCH 2/2] Update clang/bindings/python/tests/cindex/test_cursor.py Co-authored-by: Jannick Kremer --- clang/bindings/python/tests/cindex/test_cursor.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clang/bindings/python/tests/cindex/test_cursor.py b/clang/bindings/python/tests/cindex/test_cursor.py index 927e35993a1c37..24ce15ba786428 100644 --- a/clang/bindings/python/tests/cindex/test_cursor.py +++ b/clang/bindings/python/tests/cindex/test_cursor.py @@ -572,7 +572,7 @@ def test_enum_values_cpp(self): def test_enum_values_on_elaborated_type(self): tu = get_tu( -"using myUType = unsigned char; enum TEST : myUType { SPAM = 1, HAM = 0xff; }", +"using myUType = unsigned char; enum TEST : myUType { SPAM = 1, HAM = 0xff };", lang="cpp", ) enum = get_cursor(tu, "TEST") ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] Fix evaluation of the unsigned enumeration values, #108766 (PR #108769)
https://github.com/fursov updated https://github.com/llvm/llvm-project/pull/108769 >From 4d9f2e2e9ac57b4279a4aafc078a3b4fb3ff646f Mon Sep 17 00:00:00 2001 From: Dmitry Fursov Date: Sun, 15 Sep 2024 18:14:48 +0300 Subject: [PATCH] Fix evaluation of the unsigned enumeration values If the type of an enumeration is not native (e.g. uint8_t) but is unsigned then evaluation of the value might get wrong: for values bigger than half of type range the return value will be negative. This happens because for non-native enum types the TypeKind is ELABORATED. To get the real integer type, the conversion to canonical type is required before the enum_value() function can decide about type being signed or unsigned. --- clang/bindings/python/clang/cindex.py | 2 ++ .../python/tests/cindex/test_cursor.py| 20 +++ 2 files changed, 22 insertions(+) diff --git a/clang/bindings/python/clang/cindex.py b/clang/bindings/python/clang/cindex.py index 4da99e899e7f7c..c582c82ce50789 100644 --- a/clang/bindings/python/clang/cindex.py +++ b/clang/bindings/python/clang/cindex.py @@ -1952,6 +1952,8 @@ def enum_value(self): underlying_type = self.type if underlying_type.kind == TypeKind.ENUM: underlying_type = underlying_type.get_declaration().enum_type +if underlying_type.kind == TypeKind.ELABORATED: +underlying_type = underlying_type.get_canonical() if underlying_type.kind in ( TypeKind.CHAR_U, TypeKind.UCHAR, diff --git a/clang/bindings/python/tests/cindex/test_cursor.py b/clang/bindings/python/tests/cindex/test_cursor.py index 7476947bde2ea6..927e35993a1c37 100644 --- a/clang/bindings/python/tests/cindex/test_cursor.py +++ b/clang/bindings/python/tests/cindex/test_cursor.py @@ -570,6 +570,26 @@ def test_enum_values_cpp(self): self.assertEqual(ham.kind, CursorKind.ENUM_CONSTANT_DECL) self.assertEqual(ham.enum_value, 0x100) +def test_enum_values_on_elaborated_type(self): +tu = get_tu( +"using myUType = unsigned char; enum TEST : myUType { SPAM = 1, HAM = 0xff; }", +lang="cpp", +) +enum = get_cursor(tu, "TEST") +self.assertIsNotNone(enum) + +self.assertEqual(enum.kind, CursorKind.ENUM_DECL) + +enum_constants = list(enum.get_children()) +self.assertEqual(len(enum_constants), 2) + +spam, ham = enum_constants + +self.assertEqual(spam.kind, CursorKind.ENUM_CONSTANT_DECL) +self.assertEqual(spam.enum_value, 1) +self.assertEqual(ham.kind, CursorKind.ENUM_CONSTANT_DECL) +self.assertEqual(ham.enum_value, 255) + def test_annotation_attribute(self): tu = get_tu( 'int foo (void) __attribute__ ((annotate("here be annotation attribute")));' ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits