Merge branch 'master' into merge

This commit is contained in:
Arseny Kapoulkine 2022-08-04 14:27:40 -07:00
commit 33190f84ae
10 changed files with 118 additions and 15 deletions

View File

@ -1433,7 +1433,7 @@ private:
const char* checkStringFormat(const char* data, size_t size)
{
const char* flags = "-+ #0";
const char* options = "cdiouxXeEfgGqs";
const char* options = "cdiouxXeEfgGqs*";
for (size_t i = 0; i < size; ++i)
{

View File

@ -1058,7 +1058,7 @@ ConstrainedTypeVarIterator end(const ConstrainedTypeVar* ctv)
static std::vector<TypeId> parseFormatString(TypeChecker& typechecker, const char* data, size_t size)
{
const char* options = "cdiouxXeEfgGqs";
const char* options = "cdiouxXeEfgGqs*";
std::vector<TypeId> result;
@ -1072,7 +1072,7 @@ static std::vector<TypeId> parseFormatString(TypeChecker& typechecker, const cha
continue;
// we just ignore all characters (including flags/precision) up until first alphabetic character
while (i < size && !(data[i] > 0 && isalpha(data[i])))
while (i < size && !(data[i] > 0 && (isalpha(data[i]) || data[i] == '*')))
i++;
if (i == size)
@ -1080,6 +1080,8 @@ static std::vector<TypeId> parseFormatString(TypeChecker& typechecker, const cha
if (data[i] == 'q' || data[i] == 's')
result.push_back(typechecker.stringType);
else if (data[i] == '*')
result.push_back(typechecker.unknownType);
else if (strchr(options, data[i]))
result.push_back(typechecker.numberType);
else

View File

@ -20,6 +20,9 @@
#ifdef _WIN32
#include <io.h>
#include <fcntl.h>
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#endif
#ifdef CALLGRIND
@ -27,6 +30,7 @@
#endif
#include <locale.h>
#include <signal.h>
LUAU_FASTFLAG(DebugLuauTimeTracing)
@ -47,6 +51,35 @@ enum class CompileFormat
constexpr int MaxTraversalLimit = 50;
// Ctrl-C handling
static void sigintCallback(lua_State* L, int gc)
{
if (gc >= 0)
return;
lua_callbacks(L)->interrupt = NULL;
lua_rawcheckstack(L, 1); // reserve space for error string
luaL_error(L, "Execution interrupted");
}
static lua_State* replState = NULL;
#ifdef _WIN32
BOOL WINAPI sigintHandler(DWORD signal)
{
if (signal == CTRL_C_EVENT && replState)
lua_callbacks(replState)->interrupt = &sigintCallback;
return TRUE;
}
#else
static void sigintHandler(int signum)
{
if (signum == SIGINT && replState)
lua_callbacks(replState)->interrupt = &sigintCallback;
}
#endif
struct GlobalOptions
{
int optimizationLevel = 1;
@ -535,6 +568,15 @@ static void runRepl()
lua_State* L = globalState.get();
setupState(L);
// setup Ctrl+C handling
replState = L;
#ifdef _WIN32
SetConsoleCtrlHandler(sigintHandler, TRUE);
#else
signal(SIGINT, sigintHandler);
#endif
luaL_sandboxthread(L);
runReplImpl(L);
}

View File

@ -8,6 +8,8 @@
#include <string.h>
#include <stdio.h>
LUAU_FASTFLAGVARIABLE(LuauTostringFormatSpecifier, false);
/* macro to `unsign' a character */
#define uchar(c) ((unsigned char)(c))
@ -1032,6 +1034,26 @@ static int str_format(lua_State* L)
break;
}
}
case '*':
{
if (!FFlag::LuauTostringFormatSpecifier)
{
luaL_error(L, "invalid option '%%*' to 'format'");
break;
}
if (formatItemSize != 1)
{
luaL_error(L, "'%%*' does not take a form");
}
size_t length;
const char* string = luaL_tolstring(L, arg, &length);
luaL_addlstring(&b, string, length);
continue; /* skip the `addsize' at the end */
}
default:
{ /* also treat cases `pnLlh' */
luaL_error(L, "invalid option '%%%c' to 'format'", *(strfrmt - 1));

View File

@ -26,15 +26,3 @@ This document tracks unimplemented RFCs.
[RFC: Lower bounds calculation](https://github.com/Roblox/luau/blob/master/rfcs/lower-bounds-calculation.md)
**Status**: Implemented but not fully rolled out yet.
## never and unknown types
[RFC: never and unknown types](https://github.com/Roblox/luau/blob/master/rfcs/never-and-unknown-types.md)
**Status**: Needs implementation
## __len metamethod for tables and rawlen function
[RFC: Support __len metamethod for tables and rawlen function](https://github.com/Roblox/luau/blob/master/rfcs/len-metamethod-rawlen.md)
**Status**: Needs implementation

View File

@ -1,5 +1,7 @@
# Support `__len` metamethod for tables and `rawlen` function
**Status**: Implemented
## Summary
`__len` metamethod will be called by `#` operator on tables, matching Lua 5.2

View File

@ -1,5 +1,7 @@
# never and unknown types
**Status**: Implemented
## Summary
Add `unknown` and `never` types that are inhabited by everything and nothing respectively.

View File

@ -291,6 +291,8 @@ TEST_CASE("Clear")
TEST_CASE("Strings")
{
ScopedFastFlag sff{"LuauTostringFormatSpecifier", true};
runConformance("strings.lua");
}

View File

@ -556,6 +556,29 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "string_format_correctly_ordered_types")
CHECK_EQ(tm->givenType, typeChecker.numberType);
}
TEST_CASE_FIXTURE(BuiltinsFixture, "string_format_tostring_specifier")
{
CheckResult result = check(R"(
--!strict
string.format("%* %* %* %*", "string", 1, true, function() end)
)");
LUAU_REQUIRE_NO_ERRORS(result);
}
TEST_CASE_FIXTURE(BuiltinsFixture, "string_format_tostring_specifier_type_constraint")
{
CheckResult result = check(R"(
local function f(x): string
local _ = string.format("%*", x)
return x
end
)");
LUAU_REQUIRE_NO_ERRORS(result);
CHECK_EQ("(string) -> string", toString(requireType("f")));
}
TEST_CASE_FIXTURE(BuiltinsFixture, "xpcall")
{
CheckResult result = check(R"(

View File

@ -130,6 +130,26 @@ assert(string.format('"-%20s.20s"', string.rep("%", 2000)) ==
-- longest number that can be formated
assert(string.len(string.format('%99.99f', -1e308)) >= 100)
local function return_one_thing()
return "hi"
end
local function return_two_nils()
return nil, nil
end
assert(string.format("%*", return_one_thing()) == "hi")
assert(string.format("%* %*", return_two_nils()) == "nil nil")
assert(pcall(function()
string.format("%* %* %*", return_two_nils())
end) == false)
assert(string.format("%*", "a\0b\0c") == "a\0b\0c")
assert(string.format("%*", string.rep("doge", 3000)) == string.rep("doge", 3000))
assert(pcall(function()
string.format("%#*", "bad form")
end) == false)
assert(loadstring("return 1\n--comentário sem EOL no final")() == 1)