Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
52 changes: 49 additions & 3 deletions compiler/code-generator.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1139,7 +1139,7 @@ CodeGenerator::EmitFieldAccessExpr(FieldAccessExpr* expr)

void CodeGenerator::EmitCallExpr(CallExpr* call) {
// If returning an array, push a hidden parameter.
if (call->fun()->return_array()) {
if (call->fun()->return_array() && !call->fun()->is_native()) {
cell retsize = call->fun()->return_type()->CellStorageSize();
assert(retsize);

Expand Down Expand Up @@ -1208,12 +1208,58 @@ void CodeGenerator::EmitCallExpr(CallExpr* call) {
__ emit(OP_PUSH_PRI);
}

EmitCall(call->fun(), (cell)argv.size());
cell_t hidden_args = 0;
if (call->fun()->return_array() && call->fun()->is_native()) {
EmitNativeCallHiddenArg(call);
hidden_args++;
}

EmitCall(call->fun(), (cell)argv.size() + hidden_args);

if (call->fun()->return_array())
if (call->fun()->return_array() && !call->fun()->is_native())
__ emit(OP_POP_PRI);
}

void CodeGenerator::EmitNativeCallHiddenArg(CallExpr* call) {
TrackTempHeapAlloc(call, 1);

auto fun = call->fun();

ArrayData array;
BuildCompoundInitializer(fun->return_type(), nullptr, &array);

cell retsize = call->fun()->return_type()->CellStorageSize();
assert(retsize);

__ emit(OP_HEAP, retsize * sizeof(cell));

auto info = fun->return_array();
if (array.iv.empty()) {
__ emit(OP_CONST_PRI, 0);
__ emit(OP_FILL, retsize);
} else {
if (!info->iv_size) {
// No initializer, so we should have no data.
assert(array.data.empty());
assert(array.zeroes);

info->iv_size = (cell_t)array.iv.size();
info->dat_addr = data_.dat_address();
info->zeroes = array.zeroes;
data_.Add(std::move(array.iv));
}

cell dat_addr = info->dat_addr;
cell iv_size = info->iv_size;
assert(iv_size);
assert(info->zeroes);

__ emit(OP_INITARRAY_ALT, dat_addr, iv_size, 0, info->zeroes, 0);
}

__ emit(OP_PUSH_ALT);
}

void
CodeGenerator::EmitDefaultArgExpr(DefaultArgExpr* expr)
{
Expand Down
1 change: 1 addition & 0 deletions compiler/code-generator.h
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,7 @@ class CodeGenerator final
void EmitIndexExpr(IndexExpr* expr);
void EmitFieldAccessExpr(FieldAccessExpr* expr);
void EmitCallExpr(CallExpr* expr);
void EmitNativeCallHiddenArg(CallExpr* expr);
void EmitDefaultArgExpr(DefaultArgExpr* expr);
void EmitCallUserOpExpr(CallUserOpExpr* expr);
void EmitNewArrayExpr(NewArrayExpr* expr);
Expand Down
4 changes: 2 additions & 2 deletions compiler/messages.h
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ static const char* errmsg[] = {
/*036*/ "empty statement\n",
/*037*/ "invalid string (possibly non-terminated string)\n",
/*038*/ "extra characters on line\n",
/*039*/ "unused39\n",
/*039*/ "natives can only return fixed-size arrays\n",
/*040*/ "duplicate \"case\" label (value %d)\n",
/*041*/ "invalid ellipsis, array size is not known\n",
/*042*/ "invalid combination of class specifiers\n",
Expand Down Expand Up @@ -165,7 +165,7 @@ static const char* errmsg[] = {
/*138*/ "const was specified twice\n",
/*139*/ "could not find type \"%s\"\n",
/*140*/ "function '%s' does not return a value\n",
/*141*/ "natives, forwards, and public functions cannot return arrays\n",
/*141*/ "forwards and public functions cannot return arrays\n",
/*142*/ "unexpected array expression\n",
/*143*/ "new-style declarations should not have \"new\"\n",
/*144*/ "void cannot be used as a variable type\n",
Expand Down
6 changes: 0 additions & 6 deletions compiler/name-resolution.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -790,12 +790,6 @@ bool FunctionDecl::Bind(SemaContext& outer_sc) {
markusage(args_[0], uREAD);
}

if ((is_native_ || is_public_ || is_forward_) &&
(return_type()->isArray() || return_type()->isEnumStruct()))
{
error(pos_, 141);
}

ok &= BindArgs(sc);

if (body_)
Expand Down
23 changes: 20 additions & 3 deletions compiler/semantics.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2522,6 +2522,24 @@ bool Semantics::CheckCompoundReturnStmt(ReturnStmt* stmt) {
return true;
}

bool Semantics::CheckNativeCompoundReturn(FunctionDecl* info) {
auto rt = info->return_type();
if (auto root = rt->as<ArrayType>()) {
for (auto it = root; it; it = it->inner()->as<ArrayType>()) {
if (it->size() == 0) {
report(info, 39);
return false;
}
}
}

// For native calls, the implicit arg is first, not last.
auto rai = new FunctionDecl::ReturnArrayInfo;
rai->hidden_address = sizeof(cell_t) * 3;
info->set_return_array(rai);
return true;
}

bool Semantics::CheckAssertStmt(AssertStmt* stmt) {
if (Expr* expr = AnalyzeForTest(stmt->expr())) {
stmt->set_expr(expr);
Expand Down Expand Up @@ -2857,10 +2875,9 @@ bool Semantics::CheckFunctionDeclImpl(FunctionDecl* info) {
}

if (info->is_native()) {
if (decl.type.dim_exprs.size() > 0) {
report(info->pos(), 83);
auto rt = info->return_type();
if ((rt->isArray() || rt->isEnumStruct()) && !CheckNativeCompoundReturn(info))
return false;
}
return true;
}

Expand Down
1 change: 1 addition & 0 deletions compiler/semantics.h
Original file line number Diff line number Diff line change
Expand Up @@ -184,6 +184,7 @@ class Semantics final
bool CheckStaticAssertStmt(StaticAssertStmt* stmt);
bool CheckReturnStmt(ReturnStmt* stmt);
bool CheckCompoundReturnStmt(ReturnStmt* stmt);
bool CheckNativeCompoundReturn(FunctionDecl* info);
bool CheckExprStmt(ExprStmt* stmt);
bool CheckIfStmt(IfStmt* stmt);
bool CheckConstDecl(ConstDecl* decl);
Expand Down
6 changes: 6 additions & 0 deletions include/sp_typeutil.h
Original file line number Diff line number Diff line change
Expand Up @@ -77,4 +77,10 @@ sp_ctof(cell_t val)
return sp::FloatCellUnion(val).f32;
}

template <size_t Size>
struct CharArraySize {
static constexpr size_t cells = (Size + sizeof(cell_t) - 1) / sizeof(cell_t);
static constexpr size_t bytes = cells * sizeof(cell_t);
};

#endif //_INCLUDE_SOURCEPAWN_VM_TYPEUTIL_H_
4 changes: 2 additions & 2 deletions tests/compile-only/fail-newdecls-2.txt
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
(1) : error 141: natives, forwards, and public functions cannot return arrays
(2) : error 141: natives, forwards, and public functions cannot return arrays
(1) : error 039: natives can only return fixed-size arrays
(2) : error 141: forwards and public functions cannot return arrays
2 changes: 2 additions & 0 deletions tests/enum-structs/native-return.out
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
x: 4
y: 6
8 changes: 8 additions & 0 deletions tests/enum-structs/native-return.sp
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
#include <shell>

public main()
{
TestStruct a = {1, 2};
TestStruct b = {3, 4};
print_test_struct(add_test_structs(a, b));
}
1 change: 1 addition & 0 deletions tests/shell.inc
Original file line number Diff line number Diff line change
Expand Up @@ -52,3 +52,4 @@ native void copy_2d_array_to_callback(const int[] flat_array, int length, int st
native void assert_eq(any a1, any a2);

native void print_test_struct(TestStruct x);
native TestStruct add_test_structs(TestStruct a, TestStruct b);
2 changes: 1 addition & 1 deletion third_party/amtl
29 changes: 29 additions & 0 deletions vm/shell/shell.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -327,6 +327,24 @@ static cell_t PrintTestStruct(IPluginContext* cx, const cell_t* params) {
return 0;
}

static cell_t AddTestStructs(IPluginContext* cx, const cell_t* params) {
TestStruct* a;
TestStruct* b;
TestStruct* out;

int err;
if ((err = cx->LocalToPhysAddr(params[1], reinterpret_cast<cell_t**>(&out))) != SP_ERROR_NONE)
return cx->ThrowNativeErrorEx(err, "Could not read out argument");
if ((err = cx->LocalToPhysAddr(params[2], reinterpret_cast<cell_t**>(&a))) != SP_ERROR_NONE)
return cx->ThrowNativeErrorEx(err, "Could not read argument 1");
if ((err = cx->LocalToPhysAddr(params[3], reinterpret_cast<cell_t**>(&b))) != SP_ERROR_NONE)
return cx->ThrowNativeErrorEx(err, "Could not read argument 2");

out->x = a->x + b->x;
out->y = a->y + b->y;
return params[1];
}

class DynamicNative : public INativeCallback
{
public:
Expand All @@ -353,6 +371,16 @@ class DynamicNative : public INativeCallback
uintptr_t refcount_ = 0;
};

#pragma pack(push, 1)
struct LayoutVerifier {
char message[CharArraySize<50>::bytes];
int x;
};
#pragma pack(pop)

static_assert(offsetof(LayoutVerifier, message) == 0);
static_assert(offsetof(LayoutVerifier, x) == 52);

static int Execute(const char* file)
{
char error[255];
Expand Down Expand Up @@ -386,6 +414,7 @@ static int Execute(const char* file)
BindNative(rt, "assert_eq", AssertEq);
BindNative(rt, "printf", Printf);
BindNative(rt, "print_test_struct", PrintTestStruct);
BindNative(rt, "add_test_structs", AddTestStructs);

IPluginFunction* fun = rt->GetFunctionByName("main");
if (!fun)
Expand Down
Loading