Repository: mesonbuild/meson

Issue:
macOS: cpp_std does not accept all supported versions
**Describe the bug**
If I set `cpp_std=c++20` on macOS, Meson rejects the specified C++ standard after hitting the line `add_languages('objcpp')`.

**To Reproduce**
A simple repro:
```
project('repro', 'cpp',
    default_options: [
        'cpp_std=c++20'
    ]
)

add_languages('objcpp')
```

```
$ meson setup build .
The Meson build system
Version: 1.1.1
Source dir: ...
Build dir: .../build
Build type: native build
Project name: repro
Project version: undefined
C++ compiler for the host machine: ccache c++ (clang 15.0.0 "Apple clang version 15.0.0 (clang-1500.0.28.1.1)")
C++ linker for the host machine: c++ ld64 902.11
Host machine cpu family: aarch64
Host machine cpu: aarch64

meson.build:7:0: ERROR: Value "c++20" (of type "string") for combo option "C++ language standard to use" is not one of the choices. Possible choices are (as string): "none", "c++98", "c++11", "c++14", "c++17", "gnu++98", "gnu++11", "gnu++14", "gnu++17".

A full log can be found at .../meson-log.txt
```

The referenced meson-log.txt has nothing interesting to say about the failed check, presumably it doesn't shell out at all to do it.

**Expected behavior**
Meson should accept the specified C++ standard version, especially since the compiler seems to have no problem with it.

In C++ mode:
```
$ clang -E -dM -xc++ -std=c++20 -o - /dev/null | grep cplus
#define __cplusplus 202002L
```
And even in Objective-C++ mode:
```
$ clang -E -dM -xobjective-c++ -std=c++20 -o - /dev/null | grep cplus
#define __cplusplus 202002L
```

I suspect what's happening is that Meson is using some hardcoded list of accepted C++ standard names. I don't know if there's a better way to query the supported `-std=` flags, but if you do provide a bogus one, it spews the valid values to stderr, in a human-readable form:

```
$ clang -E -dM -xc++ -std=nonsense -o - /dev/null
error: invalid value 'nonsense' in '-std=nonsense'
note: use 'c++98' or 'c++03' for 'ISO C++ 1998 with amendments' standard
note: use 'gnu++98' or 'gnu++03' for 'ISO C++ 1998 with amendments and GNU extensions' standard
note: use 'c++11' for 'ISO C++ 2011 with amendments' standard
note: use 'gnu++11' for 'ISO C++ 2011 with amendments and GNU extensions' standard
note: use 'c++14' for 'ISO C++ 2014 with amendments' standard
note: use 'gnu++14' for 'ISO C++ 2014 with amendments and GNU extensions' standard
note: use 'c++17' for 'ISO C++ 2017 with amendments' standard
note: use 'gnu++17' for 'ISO C++ 2017 with amendments and GNU extensions' standard
note: use 'c++20' for 'ISO C++ 2020 DIS' standard
note: use 'gnu++20' for 'ISO C++ 2020 DIS with GNU extensions' standard
note: use 'c++2b' for 'Working draft for ISO C++ 2023 DIS' standard
note: use 'gnu++2b' for 'Working draft for ISO C++ 2023 DIS with GNU extensions' standard
```

Given that upstream Clang [has a test](https://github.com/llvm/llvm-project/blob/main/clang/test/Driver/unknown-std.cpp) to ensure this output stays stable, it might be reasonable to parse it out to find the valid options instead of hardcoding them.

**system parameters**
OS and Xcode version:
* macOS 14.0 beta (23A5257q)
* Xcode 15.0 beta (15A5160n)

Also affects macOS 13.4 with Xcode 14.3.1.

Meson, ninja, and Python interpreter version:
```
$ meson --version
1.1.1
$ ninja --version
1.11.1
$ /opt/homebrew/opt/python@3.11/bin/python3.11 --version
Python 3.11.3
```


Discussion:
This doesn't make sense https://github.com/mesonbuild/meson/blob/master/mesonbuild/compilers/cpp.py#L213-L217
The list meson allows looks closer to this one:

https://github.com/mesonbuild/meson/blob/e869a09bc2e0fbd988fdc19014b8b0ad7e4b97c9/mesonbuild/compilers/cpp.py#L335-L338

Wonder if it's detecting Apple Clang incorrectly?
If you could do some debugging, that would be useful. I don't have a Mac to help out
Ah, ran with `python -m trace --trace --module meson setup build .` and the list comes from here, actually:

https://github.com/mesonbuild/meson/blob/e869a09bc2e0fbd988fdc19014b8b0ad7e4b97c9/mesonbuild/compilers/objcpp.py#L96-L102

So that makes more sense. Strange that the list excludes newer C++ standards, or that `cpp_std` is overloaded to imply both C++ and Objective-C++.
Then do we just need a PR which adds those things to the list 
I think that would work for the short term.

Though I wonder if perhaps it should also respect some new `objcpp_std` option (and if that's not present, pull from `cpp_std`) or something like that.
This would probably do for the short-term fix to add all the missing C++ standard versions in the various compilers (including "c++2b", which will be c++23):

```diff
diff --git a/mesonbuild/compilers/cpp.py b/mesonbuild/compilers/cpp.py
index dbdcb2fa2..89d385725 100644
--- a/mesonbuild/compilers/cpp.py
+++ b/mesonbuild/compilers/cpp.py
@@ -154,6 +154,8 @@ class CPPCompiler(CLikeCompiler, Compiler):
             'gnu++17': 'gnu++1z',
             'c++20': 'c++2a',
             'gnu++20': 'gnu++2a',
+            'c++23': 'c++2b',
+            'gnu++23': 'gnu++2b',
         }

         # Currently, remapping is only supported for Clang, Elbrus and GCC
@@ -212,8 +214,8 @@ class ClangCPPCompiler(ClangCompiler, CPPCompiler):
         })
         opts[key.evolve('std')].choices = [
             'none', 'c++98', 'c++03', 'c++11', 'c++14', 'c++17', 'c++1z',
-            'c++2a', 'c++20', 'gnu++11', 'gnu++14', 'gnu++17', 'gnu++1z',
-            'gnu++2a', 'gnu++20',
+            'c++2a', 'c++20', 'c++2b', 'gnu++11', 'gnu++14', 'gnu++17', 'gnu++1z',
+            'gnu++2a', 'gnu++20', 'gnu++2b'
         ]
         if self.info.is_windows() or self.info.is_cygwin():
             opts.update({
@@ -388,8 +390,8 @@ class GnuCPPCompiler(GnuCompiler, CPPCompiler):
         })
         cppstd_choices = [
             'none', 'c++98', 'c++03', 'c++11', 'c++14', 'c++17', 'c++1z',
-            'c++2a', 'c++20', 'gnu++03', 'gnu++11', 'gnu++14', 'gnu++17',
-            'gnu++1z', 'gnu++2a', 'gnu++20',
+            'c++2a', 'c++20', 'c++2b', 'gnu++03', 'gnu++11', 'gnu++14', 'gnu++17',
+            'gnu++1z', 'gnu++2a', 'gnu++20', 'gnu++2b'
         ]
         if version_compare(self.version, '>=12.2.0'):
             cppstd_choices.append('c++23')
diff --git a/mesonbuild/compilers/objcpp.py b/mesonbuild/compilers/objcpp.py
index 1f9f75635..b46718e2e 100644
--- a/mesonbuild/compilers/objcpp.py
+++ b/mesonbuild/compilers/objcpp.py
@@ -96,7 +96,9 @@ class ClangObjCPPCompiler(ClangCompiler, ObjCPPCompiler):
         opts.update({
             OptionKey('std', machine=self.for_machine, lang='cpp'): coredata.UserComboOption(
                 'C++ language standard to use',
-                ['none', 'c++98', 'c++11', 'c++14', 'c++17', 'gnu++98', 'gnu++11', 'gnu++14', 'gnu++17'],
+                ['none', 'c++98', 'c++11', 'c++14', 'c++17', 'c++20', 'c++2b',
+                 'gnu++98', 'gnu++11', 'gnu++14', 'gnu++17', 'gnu++20',
+                 'gnu++2b'],
                 'none',
             )
         })
```
If you could come up with a PR, that would be great.
Sure, have some other work to do but I'll submit one soon
No need to rush. Would be great to have it for 1.2.0 however

Resolving patch:
diff --git a/mesonbuild/compilers/objcpp.py b/mesonbuild/compilers/objcpp.py
--- a/mesonbuild/compilers/objcpp.py
+++ b/mesonbuild/compilers/objcpp.py
@@ -96,7 +96,9 @@ def get_options(self) -> 'coredata.MutableKeyedOptionDictType':
         opts.update({
             OptionKey('std', machine=self.for_machine, lang='cpp'): coredata.UserComboOption(
                 'C++ language standard to use',
-                ['none', 'c++98', 'c++11', 'c++14', 'c++17', 'gnu++98', 'gnu++11', 'gnu++14', 'gnu++17'],
+                ['none', 'c++98', 'c++11', 'c++14', 'c++17', 'c++20', 'c++2b',
+                 'gnu++98', 'gnu++11', 'gnu++14', 'gnu++17', 'gnu++20',
+                 'gnu++2b'],
                 'none',
             )
         })


Test patch: