Kaydet (Commit) 598d3228 authored tarafından Luboš Luňák's avatar Luboš Luňák

remove HAVE_SFINAE_ANONYMOUS_BROKEN

Since we no longer support the old Apple SDK using gcc-4.0.1, we can
remove the cruft to work around its problems. Woohoo.

Change-Id: Idf275e76449443f1f0314e75dab993f213a77eb7
üst d9e0c08b
...@@ -228,7 +228,6 @@ export HAVE_LD_HASH_STYLE=@HAVE_LD_HASH_STYLE@ ...@@ -228,7 +228,6 @@ export HAVE_LD_HASH_STYLE=@HAVE_LD_HASH_STYLE@
export HAVE_NON_CONST_NPP_GETMIMEDESCRIPTION=@HAVE_NON_CONST_NPP_GETMIMEDESCRIPTION@ export HAVE_NON_CONST_NPP_GETMIMEDESCRIPTION=@HAVE_NON_CONST_NPP_GETMIMEDESCRIPTION@
export HAVE_POSIX_FALLOCATE=@HAVE_POSIX_FALLOCATE@ export HAVE_POSIX_FALLOCATE=@HAVE_POSIX_FALLOCATE@
export HAVE_READDIR_R=@HAVE_READDIR_R@ export HAVE_READDIR_R=@HAVE_READDIR_R@
export HAVE_SFINAE_ANONYMOUS_BROKEN=@HAVE_SFINAE_ANONYMOUS_BROKEN@
export HAVE_THREADSAFE_STATICS=@HAVE_THREADSAFE_STATICS@ export HAVE_THREADSAFE_STATICS=@HAVE_THREADSAFE_STATICS@
export HOST_PLATFORM=@host@ export HOST_PLATFORM=@host@
export HSQLDB_JAR=@HSQLDB_JAR@ export HSQLDB_JAR=@HSQLDB_JAR@
......
...@@ -18,7 +18,6 @@ Any change in this header will cause a rebuild of almost everything. ...@@ -18,7 +18,6 @@ Any change in this header will cause a rebuild of almost everything.
#define HAVE_GCC_BUILTIN_ATOMIC 0 #define HAVE_GCC_BUILTIN_ATOMIC 0
#define HAVE_GCC_PRAGMA_DIAGNOSTIC_MODIFY 0 #define HAVE_GCC_PRAGMA_DIAGNOSTIC_MODIFY 0
#define HAVE_GCC_PRAGMA_DIAGNOSTIC_SCOPE 0 #define HAVE_GCC_PRAGMA_DIAGNOSTIC_SCOPE 0
#define HAVE_SFINAE_ANONYMOUS_BROKEN 0
#define HAVE_THREADSAFE_STATICS 0 #define HAVE_THREADSAFE_STATICS 0
#define HAVE_SYSLOG_H 0 #define HAVE_SYSLOG_H 0
......
...@@ -5774,42 +5774,6 @@ fi ...@@ -5774,42 +5774,6 @@ fi
AC_SUBST(HAVE_GCC_VISIBILITY_FEATURE) AC_SUBST(HAVE_GCC_VISIBILITY_FEATURE)
AC_SUBST(HAVE_GCC_VISIBILITY_BROKEN) AC_SUBST(HAVE_GCC_VISIBILITY_BROKEN)
dnl ===================================================================
dnl SFINAE test
dnl Pre-C++11 does not allow types without linkage as template arguments.
dnl Substitution Failure Is Not An Error is an idiom that disables
dnl template instances that would cause an error, without actually
dnl causing an error. Old gcc (pre-4.0.2) however causes a real error.
dnl http://gcc.gnu.org/bugzilla/show_bug.cgi?id=21514
dnl ===================================================================
HAVE_SFINAE_ANONYMOUS_BROKEN=
if test \( "$_os" != "WINNT" -o "$WITH_MINGW" = "yes" \); then
AC_LANG_PUSH([C++])
AC_MSG_CHECKING([if SFINAE is broken with anonymous types])
AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[
enum { AnonymousEnumValue };
template< typename T > class TestPredicate {};
template<> class TestPredicate< int > { public: typedef bool Type; };
template< typename T >
bool test( const T&, typename TestPredicate< T >::Type = false )
{ return true; };
void test( ... );
]], [[
test( 10 );
test( AnonymousEnumValue );
]])],[sfinae_anonymous_broken=no],[sfinae_anonymous_broken=yes
])
AC_MSG_RESULT([$sfinae_anonymous_broken])
if test "$sfinae_anonymous_broken" = "yes"; then
HAVE_SFINAE_ANONYMOUS_BROKEN="TRUE"
AC_DEFINE(HAVE_SFINAE_ANONYMOUS_BROKEN)
fi
AC_LANG_POP([C++])
fi
AC_SUBST(HAVE_SFINAE_ANONYMOUS_BROKEN)
dnl =================================================================== dnl ===================================================================
dnl Clang++ tests dnl Clang++ tests
dnl =================================================================== dnl ===================================================================
......
...@@ -154,15 +154,6 @@ public: ...@@ -154,15 +154,6 @@ public:
@overload @overload
@since LibreOffice 3.6 @since LibreOffice 3.6
*/ */
#if HAVE_SFINAE_ANONYMOUS_BROKEN // see the OString ctors
OStringBuffer( const char* value )
: pData(NULL)
{
sal_Int32 length = rtl_str_getLength( value );
nCapacity = length + 16;
rtl_stringbuffer_newFromStr_WithLength( &pData, value, length );
}
#else
template< typename T > template< typename T >
OStringBuffer( const T& value, typename internal::CharPtrDetector< T, internal::Dummy >::Type = internal::Dummy()) OStringBuffer( const T& value, typename internal::CharPtrDetector< T, internal::Dummy >::Type = internal::Dummy())
: pData(NULL) : pData(NULL)
...@@ -203,7 +194,6 @@ public: ...@@ -203,7 +194,6 @@ public:
rtl_string_unittest_const_literal = true; rtl_string_unittest_const_literal = true;
#endif #endif
} }
#endif // HAVE_SFINAE_ANONYMOUS_BROKEN
/** /**
Constructs a string buffer so that it represents the same Constructs a string buffer so that it represents the same
...@@ -458,12 +448,6 @@ public: ...@@ -458,12 +448,6 @@ public:
@param str the characters to be appended. @param str the characters to be appended.
@return this string buffer. @return this string buffer.
*/ */
#if HAVE_SFINAE_ANONYMOUS_BROKEN
OStringBuffer & append( const sal_Char * str )
{
return append( str, rtl_str_getLength( str ) );
}
#else
template< typename T > template< typename T >
typename internal::CharPtrDetector< T, OStringBuffer& >::Type append( const T& str ) typename internal::CharPtrDetector< T, OStringBuffer& >::Type append( const T& str )
{ {
...@@ -489,7 +473,6 @@ public: ...@@ -489,7 +473,6 @@ public:
rtl_stringbuffer_insert( &pData, &nCapacity, getLength(), literal, internal::ConstCharArrayDetector< T, void >::size - 1 ); rtl_stringbuffer_insert( &pData, &nCapacity, getLength(), literal, internal::ConstCharArrayDetector< T, void >::size - 1 );
return *this; return *this;
} }
#endif
/** /**
Appends the string representation of the <code>char</code> array Appends the string representation of the <code>char</code> array
...@@ -669,12 +652,6 @@ public: ...@@ -669,12 +652,6 @@ public:
@param str a character array. @param str a character array.
@return this string buffer. @return this string buffer.
*/ */
#if HAVE_SFINAE_ANONYMOUS_BROKEN
OStringBuffer & insert( sal_Int32 offset, const sal_Char * str )
{
return insert( offset, str, rtl_str_getLength( str ) );
}
#else
template< typename T > template< typename T >
typename internal::CharPtrDetector< T, OStringBuffer& >::Type insert( sal_Int32 offset, const T& str ) typename internal::CharPtrDetector< T, OStringBuffer& >::Type insert( sal_Int32 offset, const T& str )
{ {
...@@ -700,7 +677,6 @@ public: ...@@ -700,7 +677,6 @@ public:
rtl_stringbuffer_insert( &pData, &nCapacity, offset, literal, internal::ConstCharArrayDetector< T, void >::size - 1 ); rtl_stringbuffer_insert( &pData, &nCapacity, offset, literal, internal::ConstCharArrayDetector< T, void >::size - 1 );
return *this; return *this;
} }
#endif
/** /**
Inserts the string representation of the <code>char</code> array Inserts the string representation of the <code>char</code> array
......
...@@ -165,17 +165,6 @@ public: ...@@ -165,17 +165,6 @@ public:
@param value a NULL-terminated character array. @param value a NULL-terminated character array.
*/ */
#if HAVE_SFINAE_ANONYMOUS_BROKEN
// Old gcc can try to convert anonymous enums to OString and give compile error.
// So there's no special-cased handling of string literals.
// These are inline functions and technically both variants should work
// the same in practice, so there should be no compatibility problem.
OString( const sal_Char * value ) SAL_THROW(())
{
pData = 0;
rtl_string_newFromStr( &pData, value );
}
#else
template< typename T > template< typename T >
OString( const T& value, typename internal::CharPtrDetector< T, internal::Dummy >::Type = internal::Dummy() ) SAL_THROW(()) OString( const T& value, typename internal::CharPtrDetector< T, internal::Dummy >::Type = internal::Dummy() ) SAL_THROW(())
{ {
...@@ -214,8 +203,6 @@ public: ...@@ -214,8 +203,6 @@ public:
#endif #endif
} }
#endif // HAVE_SFINAE_ANONYMOUS_BROKEN
/** /**
New string from a character buffer array. New string from a character buffer array.
...@@ -542,12 +529,6 @@ public: ...@@ -542,12 +529,6 @@ public:
@return sal_True if the strings are equal; @return sal_True if the strings are equal;
sal_False, otherwise. sal_False, otherwise.
*/ */
#if HAVE_SFINAE_ANONYMOUS_BROKEN
sal_Bool equalsIgnoreAsciiCase( const sal_Char * asciiStr ) const SAL_THROW(())
{
return rtl_str_compareIgnoreAsciiCase( pData->buffer, asciiStr ) == 0;
}
#else
template< typename T > template< typename T >
typename internal::CharPtrDetector< T, bool >::Type equalsIgnoreAsciiCase( const T& asciiStr ) const SAL_THROW(()) typename internal::CharPtrDetector< T, bool >::Type equalsIgnoreAsciiCase( const T& asciiStr ) const SAL_THROW(())
{ {
...@@ -575,7 +556,6 @@ public: ...@@ -575,7 +556,6 @@ public:
return rtl_str_compareIgnoreAsciiCase_WithLength( pData->buffer, pData->length, return rtl_str_compareIgnoreAsciiCase_WithLength( pData->buffer, pData->length,
literal, internal::ConstCharArrayDetector< T, void >::size - 1 ) == 0; literal, internal::ConstCharArrayDetector< T, void >::size - 1 ) == 0;
} }
#endif
/** /**
Perform a ASCII lowercase comparison of two strings. Perform a ASCII lowercase comparison of two strings.
......
...@@ -34,14 +34,12 @@ ...@@ -34,14 +34,12 @@
// Manually defining RTL_DISABLE_FAST_STRING allows to force turning fast string concatenation off // Manually defining RTL_DISABLE_FAST_STRING allows to force turning fast string concatenation off
// (e.g. for debugging). // (e.g. for debugging).
#ifndef RTL_DISABLE_FAST_STRING #ifndef RTL_DISABLE_FAST_STRING
#if ! HAVE_SFINAE_ANONYMOUS_BROKEN
// This feature is not part of public API and is meant to be used only internally by LibreOffice. // This feature is not part of public API and is meant to be used only internally by LibreOffice.
#ifdef LIBO_INTERNAL_ONLY #ifdef LIBO_INTERNAL_ONLY
// Enable fast string concatenation. // Enable fast string concatenation.
#define RTL_FAST_STRING #define RTL_FAST_STRING
#endif #endif
#endif #endif
#endif
// The unittest uses slightly different code to help check that the proper // The unittest uses slightly different code to help check that the proper
// calls are made. The class is put into a different namespace to make // calls are made. The class is put into a different namespace to make
......
...@@ -146,37 +146,6 @@ public: ...@@ -146,37 +146,6 @@ public:
rtl_uStringbuffer_newFromStr_WithLength( &pData, value.getStr(), value.getLength() ); rtl_uStringbuffer_newFromStr_WithLength( &pData, value.getStr(), value.getLength() );
} }
#if HAVE_SFINAE_ANONYMOUS_BROKEN // see OUString ctors
template< int N >
OUStringBuffer( const char (&literal)[ N ] )
: pData(NULL)
, nCapacity( N - 1 + 16 )
{
assert( strlen( literal ) == N - 1 );
rtl_uString_newFromLiteral( &pData, literal, N - 1, 16 );
#ifdef RTL_STRING_UNITTEST
rtl_string_unittest_const_literal = true;
#endif
}
/**
* It is an error to call this overload. Strings cannot directly use non-const char[].
* @internal
*/
template< int N >
OUStringBuffer( char (&value)[ N ] )
#ifndef RTL_STRING_UNITTEST
; // intentionally not implemented
#else
{
(void) value; // unused
pData = 0;
nCapacity = 10;
rtl_uString_newFromLiteral( &pData, "!!br0ken!!", 10, 0 ); // set to garbage
rtl_string_unittest_invalid_conversion = true;
}
#endif
#else // HAVE_SFINAE_ANONYMOUS_BROKEN
template< typename T > template< typename T >
OUStringBuffer( T& literal, typename internal::ConstCharArrayDetector< T, internal::Dummy >::Type = internal::Dummy() ) OUStringBuffer( T& literal, typename internal::ConstCharArrayDetector< T, internal::Dummy >::Type = internal::Dummy() )
: pData(NULL) : pData(NULL)
...@@ -188,7 +157,6 @@ public: ...@@ -188,7 +157,6 @@ public:
rtl_string_unittest_const_literal = true; rtl_string_unittest_const_literal = true;
#endif #endif
} }
#endif // HAVE_SFINAE_ANONYMOUS_BROKEN
#ifdef RTL_STRING_UNITTEST #ifdef RTL_STRING_UNITTEST
/** /**
...@@ -592,7 +560,7 @@ public: ...@@ -592,7 +560,7 @@ public:
sal_Unicode sz[RTL_USTR_MAX_VALUEOFBOOLEAN]; sal_Unicode sz[RTL_USTR_MAX_VALUEOFBOOLEAN];
return append( sz, rtl_ustr_valueOfBoolean( sz, b ) ); return append( sz, rtl_ustr_valueOfBoolean( sz, b ) );
} }
#if ! HAVE_SFINAE_ANONYMOUS_BROKEN
// Pointer can be automatically converted to bool, which is unwanted here. // Pointer can be automatically converted to bool, which is unwanted here.
// Explicitly delete all pointer append() overloads to prevent this // Explicitly delete all pointer append() overloads to prevent this
// (except for char* and sal_Unicode* overloads, which are handled elsewhere). // (except for char* and sal_Unicode* overloads, which are handled elsewhere).
...@@ -600,7 +568,6 @@ public: ...@@ -600,7 +568,6 @@ public:
typename internal::Enable< void, typename internal::Enable< void,
!internal::CharPtrDetector< T* >::ok && !internal::SalUnicodePtrDetector< T* >::ok >::Type !internal::CharPtrDetector< T* >::ok && !internal::SalUnicodePtrDetector< T* >::ok >::Type
append( T* ) SAL_DELETED_FUNCTION; append( T* ) SAL_DELETED_FUNCTION;
#endif
// This overload is needed because OUString has a ctor from rtl_uString*, but // This overload is needed because OUString has a ctor from rtl_uString*, but
// the bool overload above would be prefered to the conversion. // the bool overload above would be prefered to the conversion.
......
...@@ -193,39 +193,6 @@ public: ...@@ -193,39 +193,6 @@ public:
@since LibreOffice 3.6 @since LibreOffice 3.6
*/ */
#if HAVE_SFINAE_ANONYMOUS_BROKEN
// Old gcc can try to convert anonymous enums to OUString and give compile error.
// So instead have a variant for const and non-const char[].
template< int N >
OUString( const char (&literal)[ N ] )
{
// Check that the string literal is in fact N - 1 long (no embedded \0's),
// any decent compiler should optimize out calls to strlen with literals.
assert( strlen( literal ) == N - 1 );
pData = 0;
rtl_uString_newFromLiteral( &pData, literal, N - 1, 0 );
#ifdef RTL_STRING_UNITTEST
rtl_string_unittest_const_literal = true;
#endif
}
/**
* It is an error to call this overload. Strings cannot directly use non-const char[].
* @internal
*/
template< int N >
OUString( char (&value)[ N ] )
#ifndef RTL_STRING_UNITTEST
; // intentionally not implemented
#else
{
(void) value; // unused
pData = 0;
rtl_uString_newFromLiteral( &pData, "!!br0ken!!", 10, 0 ); // set to garbage
rtl_string_unittest_invalid_conversion = true;
}
#endif
#else // HAVE_SFINAE_ANONYMOUS_BROKEN
template< typename T > template< typename T >
OUString( T& literal, typename internal::ConstCharArrayDetector< T, internal::Dummy >::Type = internal::Dummy() ) OUString( T& literal, typename internal::ConstCharArrayDetector< T, internal::Dummy >::Type = internal::Dummy() )
{ {
...@@ -240,9 +207,6 @@ public: ...@@ -240,9 +207,6 @@ public:
#endif #endif
} }
#endif // HAVE_SFINAE_ANONYMOUS_BROKEN
#ifdef RTL_STRING_UNITTEST #ifdef RTL_STRING_UNITTEST
/** /**
* Only used by unittests to detect incorrect conversions. * Only used by unittests to detect incorrect conversions.
......
...@@ -76,8 +76,6 @@ CPPUNIT_TEST_SUITE_END(); ...@@ -76,8 +76,6 @@ CPPUNIT_TEST_SUITE_END();
void test::ostring::StringLiterals::checkCtors() void test::ostring::StringLiterals::checkCtors()
{ {
// string literal ctors do not work with SFINAE broken and are disabled
#if ! HAVE_SFINAE_ANONYMOUS_BROKEN
bool result_tmp; bool result_tmp;
CPPUNIT_ASSERT( CONST_CTOR_USED( "test" )); CPPUNIT_ASSERT( CONST_CTOR_USED( "test" ));
const char good1[] = "test"; const char good1[] = "test";
...@@ -115,17 +113,6 @@ void test::ostring::StringLiterals::checkCtors() ...@@ -115,17 +113,6 @@ void test::ostring::StringLiterals::checkCtors()
// Check that contents are correct and equal to the case when RTL_CONSTASCII_STRINGPARAM is used. // Check that contents are correct and equal to the case when RTL_CONSTASCII_STRINGPARAM is used.
CPPUNIT_ASSERT( rtl::OString( RTL_CONSTASCII_STRINGPARAM( "" )) == rtl::OString( "" )); CPPUNIT_ASSERT( rtl::OString( RTL_CONSTASCII_STRINGPARAM( "" )) == rtl::OString( "" ));
CPPUNIT_ASSERT( rtl::OString( RTL_CONSTASCII_STRINGPARAM( "ab" )) == rtl::OString( "ab" )); CPPUNIT_ASSERT( rtl::OString( RTL_CONSTASCII_STRINGPARAM( "ab" )) == rtl::OString( "ab" ));
#if 0
// This is currently disabled because it can't be consistent with HAVE_SFINAE_ANONYMOUS_BROKEN.
// Since the situation wasn't quite consistent even before, there should be no big harm.
// Check also that embedded \0 is included (RTL_CONSTASCII_STRINGPARAM does the same,
// const char* ctor does not, but it seems to make more sense to include it when
// it's explicitly mentioned in the string literal).
CPPUNIT_ASSERT( rtl::OString( RTL_CONSTASCII_STRINGPARAM( "\0" )) == rtl::OString( "\0" ));
CPPUNIT_ASSERT( rtl::OString( RTL_CONSTASCII_STRINGPARAM( "a\0b" )) == rtl::OString( "a\0b" ));
#endif
#endif
} }
const char test::ostring::StringLiterals::bad5[] = "test"; const char test::ostring::StringLiterals::bad5[] = "test";
...@@ -159,11 +146,9 @@ void test::ostring::StringLiterals::checkUsage() ...@@ -159,11 +146,9 @@ void test::ostring::StringLiterals::checkUsage()
rtl_string_unittest_const_literal_function = false; rtl_string_unittest_const_literal_function = false;
CPPUNIT_ASSERT_EQUAL( foo, rtl::OString() = "foo" ); CPPUNIT_ASSERT_EQUAL( foo, rtl::OString() = "foo" );
CPPUNIT_ASSERT( rtl_string_unittest_const_literal_function == true ); CPPUNIT_ASSERT( rtl_string_unittest_const_literal_function == true );
#if ! HAVE_SFINAE_ANONYMOUS_BROKEN
rtl_string_unittest_const_literal_function = false; rtl_string_unittest_const_literal_function = false;
CPPUNIT_ASSERT( FoO.equalsIgnoreAsciiCase( "fOo" )); CPPUNIT_ASSERT( FoO.equalsIgnoreAsciiCase( "fOo" ));
CPPUNIT_ASSERT( rtl_string_unittest_const_literal_function == true ); CPPUNIT_ASSERT( rtl_string_unittest_const_literal_function == true );
#endif
rtl_string_unittest_const_literal_function = false; rtl_string_unittest_const_literal_function = false;
CPPUNIT_ASSERT( foobarfoo.match( "bar", 3 )); CPPUNIT_ASSERT( foobarfoo.match( "bar", 3 ));
CPPUNIT_ASSERT( rtl_string_unittest_const_literal_function == true ); CPPUNIT_ASSERT( rtl_string_unittest_const_literal_function == true );
...@@ -264,7 +249,6 @@ void test::ostring::StringLiterals::checkNonConstUsage() ...@@ -264,7 +249,6 @@ void test::ostring::StringLiterals::checkNonConstUsage()
void test::ostring::StringLiterals::checkBuffer() void test::ostring::StringLiterals::checkBuffer()
{ {
rtl::OStringBuffer buf; rtl::OStringBuffer buf;
#if ! HAVE_SFINAE_ANONYMOUS_BROKEN
rtl_string_unittest_const_literal_function = false; rtl_string_unittest_const_literal_function = false;
buf.append( "foo" ); buf.append( "foo" );
CPPUNIT_ASSERT( rtl_string_unittest_const_literal_function == true ); CPPUNIT_ASSERT( rtl_string_unittest_const_literal_function == true );
...@@ -277,9 +261,6 @@ void test::ostring::StringLiterals::checkBuffer() ...@@ -277,9 +261,6 @@ void test::ostring::StringLiterals::checkBuffer()
buf.insert( 3, "baz" ); buf.insert( 3, "baz" );
CPPUNIT_ASSERT( rtl_string_unittest_const_literal_function == true ); CPPUNIT_ASSERT( rtl_string_unittest_const_literal_function == true );
CPPUNIT_ASSERT_EQUAL( rtl::OString( "foobazbar" ), buf.toString()); CPPUNIT_ASSERT_EQUAL( rtl::OString( "foobazbar" ), buf.toString());
#else
buf.append( "foobazbar" );
#endif
rtl::OString foobazbard( "foobazbard" ); rtl::OString foobazbard( "foobazbard" );
rtl::OString foodbazbard( "foodbazbard" ); rtl::OString foodbazbard( "foodbazbard" );
......
...@@ -59,7 +59,6 @@ struct OOO_DLLPUBLIC_XMLREADER Span { ...@@ -59,7 +59,6 @@ struct OOO_DLLPUBLIC_XMLREADER Span {
begin, length, text.getStr(), text.getLength()) == 0; begin, length, text.getStr(), text.getLength()) == 0;
} }
#if ! HAVE_SFINAE_ANONYMOUS_BROKEN
/** /**
@overload @overload
This function accepts an ASCII string literal as its argument. This function accepts an ASCII string literal as its argument.
...@@ -70,7 +69,6 @@ struct OOO_DLLPUBLIC_XMLREADER Span { ...@@ -70,7 +69,6 @@ struct OOO_DLLPUBLIC_XMLREADER Span {
assert( strlen( literal ) == rtl::internal::ConstCharArrayDetector< T >::size - 1 ); assert( strlen( literal ) == rtl::internal::ConstCharArrayDetector< T >::size - 1 );
return rtl_str_compare_WithLength( begin, length, literal, rtl::internal::ConstCharArrayDetector< T, void >::size - 1 ) == 0; return rtl_str_compare_WithLength( begin, length, literal, rtl::internal::ConstCharArrayDetector< T, void >::size - 1 ) == 0;
} }
#endif
rtl::OUString convertFromUtf8() const; rtl::OUString convertFromUtf8() const;
}; };
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment