Index: flang/runtime/character.cpp =================================================================== --- flang/runtime/character.cpp +++ flang/runtime/character.cpp @@ -456,9 +456,9 @@ to[j] = static_cast(' '); } } else if (toChars <= fromChars) { - std::memcpy(to, from, toChars * shift); + std::memcpy(to, from, toChars * sizeof(TO)); } else { - std::memcpy(to, from, fromChars * shift); + std::memcpy(to, from, fromChars * sizeof(TO)); for (std::size_t j{fromChars}; j < toChars; ++j) { to[j] = static_cast(' '); } Index: flang/unittests/RuntimeGTest/CharacterTest.cpp =================================================================== --- flang/unittests/RuntimeGTest/CharacterTest.cpp +++ flang/unittests/RuntimeGTest/CharacterTest.cpp @@ -10,6 +10,7 @@ // in Fortran. #include "../../runtime/character.h" +#include "../../runtime/descriptor.h" #include "gtest/gtest.h" #include #include @@ -134,11 +135,81 @@ std::memcpy(buf[0], x, xBytes); std::memcpy(buf[1], y, yBytes); ASSERT_EQ(cmp, expect) << "compare '" << x << "'(" << xBytes << ") to '" - << y << "'(" << yBytes << "), got " << cmp + << y << "'(" << yBytes << "'), got " << cmp << ", should be " << expect << '\n'; } } +// Test MIN() and MAX() +struct ExtremumTestCase { + const char *x, *y, *expect; +}; + +template +void RunExtremumTests(const char *which, + std::function + function, + const std::vector &testCases) { + + // Helper for creating, allocating and filling up a descriptor with data from + // a raw character literal, converted to the CHAR type used by the test. + auto CreateDescriptor = [](const char *raw) -> OwningPtr { + std::size_t length{std::strlen(raw)}; + std::basic_string converted{raw, raw + length}; + + OwningPtr descriptor{Descriptor::Create( + sizeof(CHAR), length, nullptr, 0, nullptr, CFI_attribute_allocatable)}; + if (descriptor->Allocate() != 0) { + return nullptr; + } + + std::copy( + converted.begin(), converted.end(), descriptor->OffsetElement()); + + return descriptor; + }; + + for (const auto &t : testCases) { + OwningPtr x = CreateDescriptor(t.x); + OwningPtr y = CreateDescriptor(t.y); + + ASSERT_NE(x, nullptr); + ASSERT_TRUE(x->IsAllocated()); + ASSERT_NE(y, nullptr); + ASSERT_TRUE(y->IsAllocated()); + function(*x, *y, nullptr, 0); + + std::basic_string got{ + x->OffsetElement(), x->Elements() * x->ElementBytes()}; + std::basic_string expect{t.expect, t.expect + std::strlen(t.expect)}; + EXPECT_EQ(expect, got) << which << "('" << t.x << "','" << t.y + << "') for CHARACTER(kind=" << sizeof(CHAR) + << "): got " << got.data() << ", expected " + << expect.data(); + } +} + +template struct ExtremumTests : public ::testing::Test {}; +TYPED_TEST_CASE(ExtremumTests, char); // FIXME: Enable for all CharacterTypes. + +TYPED_TEST(ExtremumTests, MinTests) { + static std::vector tests{ + {"a", "z", "a"}, + {"zaaa", "aa", "aa "}, + {"aaz", "aaaaa", "aaaaa"}, + }; + RunExtremumTests("MIN", RTNAME(CharacterMin), tests); +} + +TYPED_TEST(ExtremumTests, MaxTests) { + static std::vector tests{ + {"a", "z", "z"}, + {"zaa", "aaaaa", "zaa "}, + {"aaaaa", "aazaa", "aazaa"}, + }; + RunExtremumTests("MAX", RTNAME(CharacterMax), tests); +} + // Test search functions INDEX(), SCAN(), and VERIFY() template