200 changes: 97 additions & 103 deletions libcxx/include/string
Original file line number Diff line number Diff line change
Expand Up @@ -632,29 +632,16 @@ __basic_string_common<__b>::__throw_out_of_range() const

_LIBCPP_EXTERN_TEMPLATE(class _LIBCPP_EXTERN_TEMPLATE_TYPE_VIS __basic_string_common<true>)

#ifdef _LIBCPP_NO_EXCEPTIONS
template <class _Iter>
struct __libcpp_string_gets_noexcept_iterator_impl : public true_type {};
#elif defined(_LIBCPP_HAS_NO_NOEXCEPT)
template <class _Iter>
struct __libcpp_string_gets_noexcept_iterator_impl : public false_type {};
#else
template <class _Iter, bool = __is_cpp17_forward_iterator<_Iter>::value>
struct __libcpp_string_gets_noexcept_iterator_impl : public _LIBCPP_BOOL_CONSTANT((
noexcept(++(declval<_Iter&>())) &&
is_nothrow_assignable<_Iter&, _Iter>::value &&
noexcept(declval<_Iter>() == declval<_Iter>()) &&
noexcept(*declval<_Iter>())
)) {};

template <class _Iter>
struct __libcpp_string_gets_noexcept_iterator_impl<_Iter, false> : public false_type {};
#endif
struct __string_is_trivial_iterator : public false_type {};

template <class _Tp>
struct __string_is_trivial_iterator<_Tp*>
: public is_arithmetic<_Tp> {};

template <class _Iter>
struct __libcpp_string_gets_noexcept_iterator
: public _LIBCPP_BOOL_CONSTANT(__libcpp_is_trivial_iterator<_Iter>::value || __libcpp_string_gets_noexcept_iterator_impl<_Iter>::value) {};
struct __string_is_trivial_iterator<__wrap_iter<_Iter> >
: public __string_is_trivial_iterator<_Iter> {};

template <class _CharT, class _Traits, class _Tp>
struct __can_be_converted_to_string_view : public _BoolConstant<
Expand Down Expand Up @@ -1048,35 +1035,28 @@ public:
_LIBCPP_INLINE_VISIBILITY
void __append_default_init(size_type __n);

template <class _ForwardIterator>
_LIBCPP_METHOD_TEMPLATE_IMPLICIT_INSTANTIATION_VIS
basic_string& __append_forward_unsafe(_ForwardIterator, _ForwardIterator);
template<class _InputIterator>
_LIBCPP_METHOD_TEMPLATE_IMPLICIT_INSTANTIATION_VIS
_EnableIf
<
__is_exactly_cpp17_input_iterator<_InputIterator>::value
|| !__libcpp_string_gets_noexcept_iterator<_InputIterator>::value,
__is_exactly_cpp17_input_iterator<_InputIterator>::value,
basic_string&
>
_LIBCPP_INLINE_VISIBILITY
append(_InputIterator __first, _InputIterator __last) {
const basic_string __temp (__first, __last, __alloc());
const basic_string __temp(__first, __last, __alloc());
append(__temp.data(), __temp.size());
return *this;
}
template<class _ForwardIterator>
_LIBCPP_METHOD_TEMPLATE_IMPLICIT_INSTANTIATION_VIS
_EnableIf
<
__is_cpp17_forward_iterator<_ForwardIterator>::value
&& __libcpp_string_gets_noexcept_iterator<_ForwardIterator>::value,
__is_cpp17_forward_iterator<_ForwardIterator>::value,
basic_string&
>
_LIBCPP_INLINE_VISIBILITY
append(_ForwardIterator __first, _ForwardIterator __last) {
return __append_forward_unsafe(__first, __last);
}
append(_ForwardIterator __first, _ForwardIterator __last);

#ifndef _LIBCPP_CXX03_LANG
_LIBCPP_INLINE_VISIBILITY
Expand Down Expand Up @@ -1124,17 +1104,15 @@ public:
_LIBCPP_METHOD_TEMPLATE_IMPLICIT_INSTANTIATION_VIS
_EnableIf
<
__is_exactly_cpp17_input_iterator<_InputIterator>::value
|| !__libcpp_string_gets_noexcept_iterator<_InputIterator>::value,
__is_exactly_cpp17_input_iterator<_InputIterator>::value,
basic_string&
>
assign(_InputIterator __first, _InputIterator __last);
template<class _ForwardIterator>
_LIBCPP_METHOD_TEMPLATE_IMPLICIT_INSTANTIATION_VIS
_EnableIf
<
__is_cpp17_forward_iterator<_ForwardIterator>::value
&& __libcpp_string_gets_noexcept_iterator<_ForwardIterator>::value,
__is_cpp17_forward_iterator<_ForwardIterator>::value,
basic_string&
>
assign(_ForwardIterator __first, _ForwardIterator __last);
Expand Down Expand Up @@ -1175,17 +1153,15 @@ public:
_LIBCPP_METHOD_TEMPLATE_IMPLICIT_INSTANTIATION_VIS
_EnableIf
<
__is_exactly_cpp17_input_iterator<_InputIterator>::value
|| !__libcpp_string_gets_noexcept_iterator<_InputIterator>::value,
__is_exactly_cpp17_input_iterator<_InputIterator>::value,
iterator
>
insert(const_iterator __pos, _InputIterator __first, _InputIterator __last);
template<class _ForwardIterator>
_LIBCPP_METHOD_TEMPLATE_IMPLICIT_INSTANTIATION_VIS
_EnableIf
<
__is_cpp17_forward_iterator<_ForwardIterator>::value
&& __libcpp_string_gets_noexcept_iterator<_ForwardIterator>::value,
__is_cpp17_forward_iterator<_ForwardIterator>::value,
iterator
>
insert(const_iterator __pos, _ForwardIterator __first, _ForwardIterator __last);
Expand Down Expand Up @@ -1721,6 +1697,13 @@ private:
_LIBCPP_INLINE_VISIBILITY void __invalidate_all_iterators();
_LIBCPP_INLINE_VISIBILITY void __invalidate_iterators_past(size_type);

template<class _Tp>
_LIBCPP_INLINE_VISIBILITY
bool __addr_in_range(_Tp&& __t) const {
const volatile void *__p = _VSTD::addressof(__t);
return data() <= __p && __p <= data() + size();
}

friend basic_string operator+<>(const basic_string&, const basic_string&);
friend basic_string operator+<>(const value_type*, const basic_string&);
friend basic_string operator+<>(value_type, const basic_string&);
Expand Down Expand Up @@ -2175,9 +2158,23 @@ basic_string<_CharT, _Traits, _Allocator>::__init(_ForwardIterator __first, _For
__set_long_cap(__cap+1);
__set_long_size(__sz);
}

#ifndef _LIBCPP_NO_EXCEPTIONS
try
{
#endif // _LIBCPP_NO_EXCEPTIONS
for (; __first != __last; ++__first, (void) ++__p)
traits_type::assign(*__p, *__first);
traits_type::assign(*__p, value_type());
#ifndef _LIBCPP_NO_EXCEPTIONS
}
catch (...)
{
if (__is_long())
__alloc_traits::deallocate(__alloc(), __get_long_pointer(), __get_long_cap());
throw;
}
#endif // _LIBCPP_NO_EXCEPTIONS
}

template <class _CharT, class _Traits, class _Allocator>
Expand Down Expand Up @@ -2470,8 +2467,7 @@ template <class _CharT, class _Traits, class _Allocator>
template<class _InputIterator>
_EnableIf
<
__is_exactly_cpp17_input_iterator <_InputIterator>::value
|| !__libcpp_string_gets_noexcept_iterator<_InputIterator>::value,
__is_exactly_cpp17_input_iterator<_InputIterator>::value,
basic_string<_CharT, _Traits, _Allocator>&
>
basic_string<_CharT, _Traits, _Allocator>::assign(_InputIterator __first, _InputIterator __last)
Expand All @@ -2485,26 +2481,36 @@ template <class _CharT, class _Traits, class _Allocator>
template<class _ForwardIterator>
_EnableIf
<
__is_cpp17_forward_iterator<_ForwardIterator>::value
&& __libcpp_string_gets_noexcept_iterator<_ForwardIterator>::value,
__is_cpp17_forward_iterator<_ForwardIterator>::value,
basic_string<_CharT, _Traits, _Allocator>&
>
basic_string<_CharT, _Traits, _Allocator>::assign(_ForwardIterator __first, _ForwardIterator __last)
{
size_type __n = static_cast<size_type>(_VSTD::distance(__first, __last));
size_type __cap = capacity();
if (__cap < __n)
size_type __n = __string_is_trivial_iterator<_ForwardIterator>::value ?
static_cast<size_type>(_VSTD::distance(__first, __last)) : 0;

if (__string_is_trivial_iterator<_ForwardIterator>::value &&
(__cap >= __n || !__addr_in_range(*__first)))
{
size_type __sz = size();
__grow_by(__cap, __n - __cap, __sz, 0, __sz);
if (__cap < __n)
{
size_type __sz = size();
__grow_by(__cap, __n - __cap, __sz, 0, __sz);
}
else
__invalidate_iterators_past(__n);
pointer __p = __get_pointer();
for (; __first != __last; ++__first, ++__p)
traits_type::assign(*__p, *__first);
traits_type::assign(*__p, value_type());
__set_size(__n);
}
else
__invalidate_iterators_past(__n);
pointer __p = __get_pointer();
for (; __first != __last; ++__first, ++__p)
traits_type::assign(*__p, *__first);
traits_type::assign(*__p, value_type());
__set_size(__n);
{
const basic_string __temp(__first, __last, __alloc());
assign(__temp.data(), __temp.size());
}
return *this;
}

Expand Down Expand Up @@ -2651,39 +2657,23 @@ basic_string<_CharT, _Traits, _Allocator>::push_back(value_type __c)
traits_type::assign(*++__p, value_type());
}

template <class _Tp>
bool __ptr_in_range (const _Tp* __p, const _Tp* __first, const _Tp* __last)
{
return __first <= __p && __p < __last;
}

template <class _Tp1, class _Tp2>
bool __ptr_in_range (const _Tp1*, const _Tp2*, const _Tp2*)
{
return false;
}

template <class _CharT, class _Traits, class _Allocator>
template<class _ForwardIterator>
basic_string<_CharT, _Traits, _Allocator>&
basic_string<_CharT, _Traits, _Allocator>::__append_forward_unsafe(
_EnableIf
<
__is_cpp17_forward_iterator<_ForwardIterator>::value,
basic_string<_CharT, _Traits, _Allocator>&
>
basic_string<_CharT, _Traits, _Allocator>::append(
_ForwardIterator __first, _ForwardIterator __last)
{
static_assert(__is_cpp17_forward_iterator<_ForwardIterator>::value,
"function requires a ForwardIterator");
size_type __sz = size();
size_type __cap = capacity();
size_type __n = static_cast<size_type>(_VSTD::distance(__first, __last));
if (__n)
{
typedef typename iterator_traits<_ForwardIterator>::reference _CharRef;
_CharRef __tmp_ref = *__first;
if (__ptr_in_range(_VSTD::addressof(__tmp_ref), data(), data() + size()))
{
const basic_string __temp (__first, __last, __alloc());
append(__temp.data(), __temp.size());
}
else
if (__string_is_trivial_iterator<_ForwardIterator>::value &&
!__addr_in_range(*__first))
{
if (__cap - __sz < __n)
__grow_by(__cap, __sz + __n - __cap, __sz, __sz, 0);
Expand All @@ -2693,6 +2683,11 @@ basic_string<_CharT, _Traits, _Allocator>::__append_forward_unsafe(
traits_type::assign(*__p, value_type());
__set_size(__sz + __n);
}
else
{
const basic_string __temp(__first, __last, __alloc());
append(__temp.data(), __temp.size());
}
}
return *this;
}
Expand Down Expand Up @@ -2808,8 +2803,7 @@ template <class _CharT, class _Traits, class _Allocator>
template<class _InputIterator>
_EnableIf
<
__is_exactly_cpp17_input_iterator<_InputIterator>::value
|| !__libcpp_string_gets_noexcept_iterator<_InputIterator>::value,
__is_exactly_cpp17_input_iterator<_InputIterator>::value,
typename basic_string<_CharT, _Traits, _Allocator>::iterator
>
basic_string<_CharT, _Traits, _Allocator>::insert(const_iterator __pos, _InputIterator __first, _InputIterator __last)
Expand All @@ -2827,8 +2821,7 @@ template <class _CharT, class _Traits, class _Allocator>
template<class _ForwardIterator>
_EnableIf
<
__is_cpp17_forward_iterator<_ForwardIterator>::value
&& __libcpp_string_gets_noexcept_iterator<_ForwardIterator>::value,
__is_cpp17_forward_iterator<_ForwardIterator>::value,
typename basic_string<_CharT, _Traits, _Allocator>::iterator
>
basic_string<_CharT, _Traits, _Allocator>::insert(const_iterator __pos, _ForwardIterator __first, _ForwardIterator __last)
Expand All @@ -2842,34 +2835,35 @@ basic_string<_CharT, _Traits, _Allocator>::insert(const_iterator __pos, _Forward
size_type __n = static_cast<size_type>(_VSTD::distance(__first, __last));
if (__n)
{
typedef typename iterator_traits<_ForwardIterator>::reference _CharRef;
_CharRef __tmp_char = *__first;
if (__ptr_in_range(_VSTD::addressof(__tmp_char), data(), data() + size()))
if (__string_is_trivial_iterator<_ForwardIterator>::value &&
!__addr_in_range(*__first))
{
const basic_string __temp(__first, __last, __alloc());
return insert(__pos, __temp.data(), __temp.data() + __temp.size());
}

size_type __sz = size();
size_type __cap = capacity();
value_type* __p;
if (__cap - __sz >= __n)
{
__p = _VSTD::__to_address(__get_pointer());
size_type __n_move = __sz - __ip;
if (__n_move != 0)
traits_type::move(__p + __ip + __n, __p + __ip, __n_move);
size_type __sz = size();
size_type __cap = capacity();
value_type* __p;
if (__cap - __sz >= __n)
{
__p = _VSTD::__to_address(__get_pointer());
size_type __n_move = __sz - __ip;
if (__n_move != 0)
traits_type::move(__p + __ip + __n, __p + __ip, __n_move);
}
else
{
__grow_by(__cap, __sz + __n - __cap, __sz, __ip, 0, __n);
__p = _VSTD::__to_address(__get_long_pointer());
}
__sz += __n;
__set_size(__sz);
traits_type::assign(__p[__sz], value_type());
for (__p += __ip; __first != __last; ++__p, ++__first)
traits_type::assign(*__p, *__first);
}
else
{
__grow_by(__cap, __sz + __n - __cap, __sz, __ip, 0, __n);
__p = _VSTD::__to_address(__get_long_pointer());
const basic_string __temp(__first, __last, __alloc());
return insert(__pos, __temp.data(), __temp.data() + __temp.size());
}
__sz += __n;
__set_size(__sz);
traits_type::assign(__p[__sz], value_type());
for (__p += __ip; __first != __last; ++__p, ++__first)
traits_type::assign(*__p, *__first);
}
return begin() + __ip;
}
Expand Down
195 changes: 0 additions & 195 deletions libcxx/test/libcxx/iterators/trivial_iterators.pass.cpp

This file was deleted.

89 changes: 0 additions & 89 deletions libcxx/test/libcxx/strings/iterators.exceptions.pass.cpp

This file was deleted.

81 changes: 0 additions & 81 deletions libcxx/test/libcxx/strings/iterators.noexcept.pass.cpp

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
//===----------------------------------------------------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//

// <string>

#include <cassert>
#include <string>

struct Incomplete;
template<class T> struct Holder { T t; };

template<class T>
struct Charlike {
char ch_;
Charlike(char ch) : ch_(ch) {}
operator char() const { return ch_; }
};

int main(int, char**)
{
std::string s;
Charlike<Holder<Incomplete> > a[] = {'m', 'a', 'h', 'i'};
s.append(a, a+4);
s.assign(a, a+4);
s.insert(s.begin(), a, a+4);
s.replace(s.begin(), s.begin()+4, a, a+4);
assert(s == "mahimahi");

return 0;
}
Original file line number Diff line number Diff line change
Expand Up @@ -28,18 +28,27 @@ test(S s, It first, It last, S expected)
}

#ifndef TEST_HAS_NO_EXCEPTIONS
struct Widget { operator char() const { throw 42; } };

template <class S, class It>
void
test_exceptions(S s, It first, It last)
{
S aCopy = s;
S original = s;
typename S::iterator begin = s.begin();
typename S::iterator end = s.end();

try {
s.append(first, last);
assert(false);
}
catch (...) {}
} catch (...) {}

// Part of "no effects" is that iterators and pointers
// into the string must not have been invalidated.
LIBCPP_ASSERT(s.__invariants());
assert(s == aCopy);
assert(s == original);
assert(s.begin() == begin);
assert(s.end() == end);
}
#endif

Expand Down Expand Up @@ -176,6 +185,9 @@ int main(int, char**)
test_exceptions(S(), TIter(s, s+10, 4, TIter::TAIncrement), TIter());
test_exceptions(S(), TIter(s, s+10, 5, TIter::TADereference), TIter());
test_exceptions(S(), TIter(s, s+10, 6, TIter::TAComparison), TIter());

Widget w[100];
test_exceptions(S(), w, w+100);
}
#endif

Expand Down Expand Up @@ -204,6 +216,23 @@ int main(int, char**)
assert(s == "ABCD");
}

{ // regression-test appending to self in sneaky ways
std::string s_short = "hello";
std::string s_long = "Lorem ipsum dolor sit amet, consectetur/";
std::string s_othertype = "hello";
const unsigned char *first = reinterpret_cast<const unsigned char*>(s_othertype.data());
std::string s_sneaky = "hello";

test(s_short, s_short.data() + s_short.size(), s_short.data() + s_short.size() + 1,
std::string("hello\0", 6));
test(s_long, s_long.data() + s_long.size(), s_long.data() + s_long.size() + 1,
std::string("Lorem ipsum dolor sit amet, consectetur/\0", 41));
test(s_othertype, first + 2, first + 5, std::string("hellollo"));

s_sneaky.reserve(12);
test(s_sneaky, s_sneaky.data(), s_sneaky.data() + 6, std::string("hellohello\0", 11));
}

{ // test with a move iterator that returns char&&
typedef forward_iterator<const char*> It;
typedef std::move_iterator<It> MoveIt;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,18 +28,27 @@ test(S s, It first, It last, S expected)
}

#ifndef TEST_HAS_NO_EXCEPTIONS
struct Widget { operator char() const { throw 42; } };

template <class S, class It>
void
test_exceptions(S s, It first, It last)
{
S aCopy = s;
S original = s;
typename S::iterator begin = s.begin();
typename S::iterator end = s.end();

try {
s.assign(first, last);
assert(false);
}
catch (...) {}
} catch (...) {}

// Part of "no effects" is that iterators and pointers
// into the string must not have been invalidated.
LIBCPP_ASSERT(s.__invariants());
assert(s == aCopy);
assert(s == original);
assert(s.begin() == begin);
assert(s.end() == end);
}
#endif

Expand Down Expand Up @@ -176,6 +185,9 @@ int main(int, char**)
test_exceptions(S(), TIter(s, s+10, 4, TIter::TAIncrement), TIter());
test_exceptions(S(), TIter(s, s+10, 5, TIter::TADereference), TIter());
test_exceptions(S(), TIter(s, s+10, 6, TIter::TAComparison), TIter());

Widget w[100];
test_exceptions(S(), w, w+100);
}
#endif

Expand Down Expand Up @@ -205,5 +217,12 @@ int main(int, char**)
assert(s == "ABCD");
}

return 0;
{ // regression-test assigning to self in sneaky ways
std::string sneaky = "hello";
sneaky.resize(sneaky.capacity(), 'x');
std::string expected = sneaky + std::string(1, '\0');
test(sneaky, sneaky.data(), sneaky.data() + sneaky.size() + 1, expected);
}

return 0;
}
Original file line number Diff line number Diff line change
Expand Up @@ -30,19 +30,29 @@ test(S s, typename S::difference_type pos, It first, It last, S expected)
}

#ifndef TEST_HAS_NO_EXCEPTIONS
struct Widget { operator char() const { throw 42; } };

template <class S, class It>
void
test_exceptions(S s, typename S::difference_type pos, It first, It last)
{
typename S::const_iterator p = s.cbegin() + pos;
S aCopy = s;

S original = s;
typename S::iterator begin = s.begin();
typename S::iterator end = s.end();

try {
s.insert(p, first, last);
assert(false);
}
catch (...) {}
} catch (...) {}

// Part of "no effects" is that iterators and pointers
// into the string must not have been invalidated.
LIBCPP_ASSERT(s.__invariants());
assert(s == aCopy);
assert(s == original);
assert(s.begin() == begin);
assert(s.end() == end);
}
#endif

Expand Down Expand Up @@ -153,6 +163,9 @@ int main(int, char**)
test_exceptions(S(), 0, TIter(s, s+10, 4, TIter::TAIncrement), TIter());
test_exceptions(S(), 0, TIter(s, s+10, 5, TIter::TADereference), TIter());
test_exceptions(S(), 0, TIter(s, s+10, 6, TIter::TAComparison), TIter());

Widget w[100];
test_exceptions(S(), 0, w, w+100);
}
#endif

Expand Down Expand Up @@ -181,6 +194,19 @@ int main(int, char**)
assert(s == "ABCD");
}

{ // regression-test inserting into self in sneaky ways
std::string s_short = "hello";
std::string s_long = "Lorem ipsum dolor sit amet, consectetur/";
std::string s_othertype = "hello";
const unsigned char *first = reinterpret_cast<const unsigned char*>(s_othertype.data());

test(s_short, 0, s_short.data() + s_short.size(), s_short.data() + s_short.size() + 1,
std::string("\0hello", 6));
test(s_long, 0, s_long.data() + s_long.size(), s_long.data() + s_long.size() + 1,
std::string("\0Lorem ipsum dolor sit amet, consectetur/", 41));
test(s_othertype, 1, first + 2, first + 5, std::string("hlloello"));
}

{ // test with a move iterator that returns char&&
typedef input_iterator<const char*> It;
typedef std::move_iterator<It> MoveIt;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,20 +36,30 @@ test(S s, typename S::size_type pos1, typename S::size_type n1, It f, It l, S ex
}

#ifndef TEST_HAS_NO_EXCEPTIONS
struct Widget { operator char() const { throw 42; } };

template <class S, class It>
void
test_exceptions(S s, typename S::size_type pos1, typename S::size_type n1, It f, It l)
{
typename S::const_iterator first = s.begin() + pos1;
typename S::const_iterator last = s.begin() + pos1 + n1;
S aCopy = s;

S original = s;
typename S::iterator begin = s.begin();
typename S::iterator end = s.end();

try {
s.replace(first, last, f, l);
assert(false);
}
catch (...) {}
} catch (...) {}

// Part of "no effects" is that iterators and pointers
// into the string must not have been invalidated.
LIBCPP_ASSERT(s.__invariants());
assert(s == aCopy);
assert(s == original);
assert(s.begin() == begin);
assert(s.end() == end);
}
#endif

Expand Down Expand Up @@ -1005,6 +1015,9 @@ int main(int, char**)
test_exceptions(S("abcdefghijklmnopqrst"), 10, 5, TIter(s, s+10, 4, TIter::TAIncrement), TIter());
test_exceptions(S("abcdefghijklmnopqrst"), 10, 5, TIter(s, s+10, 5, TIter::TADereference), TIter());
test_exceptions(S("abcdefghijklmnopqrst"), 10, 5, TIter(s, s+10, 6, TIter::TAComparison), TIter());

Widget w[100];
test_exceptions(S("abcdefghijklmnopqrst"), 10, 5, w, w+100);
}
#endif

Expand Down
3 changes: 1 addition & 2 deletions libcxx/utils/libcxx/test/params.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@
'-Werror',
'-Wall',
'-Wextra',
'-Wno-deprecated-copy',
'-Wshadow',
'-Wundef',
'-Wno-unused-command-line-argument',
Expand Down Expand Up @@ -103,7 +102,7 @@ def getStdFlag(cfg, std):

Parameter(name='debug_level', choices=['', '0', '1'], type=str, default='',
help="The debugging level to enable in the test suite.",
actions=lambda debugLevel: [] if debugLevel is '' else [
actions=lambda debugLevel: [] if debugLevel == '' else [
AddFeature('debug_level={}'.format(debugLevel)),
AddCompileFlag('-D_LIBCPP_DEBUG={}'.format(debugLevel))
]),
Expand Down