vlang / v

Simple, fast, safe, compiled language for developing maintainable software. Compiles itself in <1s with zero library dependencies. Supports automatic C => V translation. https://vlang.io
MIT License
35.66k stars 2.15k forks source link

Fails to compile with -freestanding: missing main #20227

Open hholst80 opened 8 months ago

hholst80 commented 8 months ago

Describe the bug

The following program fails to compile with -freestanding

import os
import time
import net.http
import vweb

struct App {
  vweb.Context
  endpoints []string
}

@[get]
fn (mut app App) world() vweb.Result {
  for endpoint in app.endpoints {
    resp := http.get(endpoint) or { // TODO: What is the timeout?
      app.set_status(404, "Not Found")
      return app.text("Not OK")
    }
    if !(200 <= resp.status_code && resp.status_code <= 299) {
      app.set_status(404, "Not Found")
      return app.text("Not OK")
    }
  }
  app.set_status(200, "OK") // or 204 No Content
  return app.text("OK") // TODO: How to return empty body?
}

fn main() {

  mut endpoints := []string{}

  for key, value in os.environ() {
    if key.starts_with("HEALTHZ_") {
      endpoints << value
    }
  }

  println("Booting up")
  println("endpoints:")
  for endpoint in endpoints {
    println(" - ${endpoint}")
  }
  println("Boot-up OK")

  mut app := App{endpoints:endpoints}

  vweb.run_at(&app, port:1337, nr_workers:1) or {
    time.sleep(1)
    panic(err)
  }
}

Reproduction Steps

$ v -prod -freestanding f.v

Expected Behavior

It should compile, and generate a static binary.

Current Behavior

# v -prod -freestanding healthz.v
==================
/usr/include/string.h: At top level:
/usr/include/string.h:43:14: error: conflicting types for ‘memcpy’; have ‘void *(void * restrict,  const void * restrict,  size_t)’ {aka ‘void *(void * restrict,  const void * restrict,  long unsigned int)’}
   43 | extern void *memcpy (void *__restrict __dest, const void *__restrict __src,
      |              ^~~~~~
/tmp/v_0/healthz.7456571127392881962.tmp.c:642:7: note: previous declaration of ‘memcpy’ with type ‘void *(void *, void *, size_t)’ {aka ‘void *(void *, void *, long unsigned int)’}
  642 | void *memcpy(void *dest, void *src, size_t n);
      |       ^~~~~~
/usr/include/string.h:47:14: error: conflicting types for ‘memmove’; have ‘void *(void *, const void *, size_t)’ {aka ‘void *(void *, const void *, long unsigned int)’}
   47 | extern void *memmove (void *__dest, const void *__src, size_t __n)
      |              ^~~~~~~
/tmp/v_0/healthz.7456571127392881962.tmp.c:644:7: note: previous declaration of ‘memmove’ with type ‘void *(void *, void *, size_t)’ {aka ‘void *(void *, void *, long unsigned int)’}
  644 | void *memmove(void *dest, void *src, size_t n);
...
==================
(Use `v -cg` to print the entire error message)

builder error: 
==================
C error. This should never happen.

This is a compiler bug, please report it using `v bug file.v`.

https://github.com/vlang/v/issues/new/choose

You can also use #help on Discord: https://discord.gg/vlang

Possible Solution

No response

Additional Information/Context

No response

V version

V 0.4.3 b5ba122

Environment details (OS name and version, etc.)

NAME="Fedora Linux"
VERSION="38 (Thirty Eight)"
ID=fedora
VERSION_ID=38
VERSION_CODENAME=""
PLATFORM_ID="platform:f38"
PRETTY_NAME="Fedora Linux 38 (Thirty Eight)"
ANSI_COLOR="0;38;2;60;110;180"
LOGO=fedora-logo-icon
CPE_NAME="cpe:/o:fedoraproject:fedora:38"
DEFAULT_HOSTNAME="fedora"
HOME_URL="https://fedoraproject.org/"
DOCUMENTATION_URL="https://docs.fedoraproject.org/en-US/fedora/f38/system-administrators-guide/"
SUPPORT_URL="https://ask.fedoraproject.org/"
BUG_REPORT_URL="https://bugzilla.redhat.com/"
REDHAT_BUGZILLA_PRODUCT="Fedora"
REDHAT_BUGZILLA_PRODUCT_VERSION=38
REDHAT_SUPPORT_PRODUCT="Fedora"
REDHAT_SUPPORT_PRODUCT_VERSION=38
SUPPORT_END=2024-05-14

[!NOTE] You can use the 👍 reaction to increase the issue's priority for developers.

Please note that only the 👍 reaction to the issue itself counts as a vote. Other reactions and those to comments will not be taken into account.

hholst80 commented 8 months ago

Another example that fails:

[I] root@neo ~
# v run test.v
V panic: We failed you master
v hash: b5ba122
[I] root@neo ~ [1]
# v run -prod -freestanding test.v
v expects that `-prod` exists, but it does not
[I] root@neo ~ [1]
# v -prod -freestanding test.v
==================
<artificial>:(.text+0x1): undefined reference to `main'
collect2: error: ld returned 1 exit status
...
==================
(Use `v -cg` to print the entire error message)

builder error: 
==================
C error. This should never happen.

This is a compiler bug, please report it using `v bug file.v`.

https://github.com/vlang/v/issues/new/choose

You can also use #help on Discord: https://discord.gg/vlang

[I] root@neo ~ [1]
# cat test.v
fn doit() !int {
    return error("nope!")
}

fn main() {
    doit() or { panic("We failed you master") }
}
[I] root@neo ~
# 
JalonSolov commented 8 months ago

The first one is understandable... the description of -freestanding is:

   -freestanding
      Build the executable without dependency on libc.
      Supported only on `linux` targets currently.

Since memcpy and memmove are defined in libc, this means you would have to create your own versions of these routines.

The 2nd, however, doesn't make much sense. Definitely needs to be fixed.

hholst80 commented 8 months ago

I think the -freestanding can easily be confused with -static which is then another thing. (Conceptually, perhaps -freestanding is more like -nostdlib in gcc?)

Ignore the first code in this issue for now then.

JalonSolov commented 8 months ago

Yes, -freestanding in V is more like -nostdlib. -cflags -static will get you a statically linked executable.

hholst80 commented 8 months ago

Makes sense. A better repro for this is then the empty program:

// empty.v
fn main() {
}
[I] root@trump ~/v (master) [1]
# v -cg -prod -freestanding empty.v
/tmp/v_0/empty.11392888721645151286.tmp.c: In function ‘_wyr8’:
/tmp/v_0/empty.11392888721645151286.tmp.c:540:81: warning: passing argument 2 of ‘memcpy’ discards ‘const’ qualifier from pointer target type [-Wdiscarded-qualifiers]
  540 |         static inline uint64_t _wyr8(const uint8_t *p) { uint64_t v; memcpy(&v, p, 8); return __builtin_bswap64(v);}
      |                                                                                 ^
/tmp/v_0/empty.11392888721645151286.tmp.c:439:32: note: expected ‘void *’ but argument is of type ‘const uint8_t *’ {aka ‘const unsigned char *’}
  439 | void *memcpy(void *dest, void *src, size_t n);
      |                          ~~~~~~^~~
/tmp/v_0/empty.11392888721645151286.tmp.c: In function ‘_wyr4’:
/tmp/v_0/empty.11392888721645151286.tmp.c:541:81: warning: passing argument 2 of ‘memcpy’ discards ‘const’ qualifier from pointer target type [-Wdiscarded-qualifiers]
  541 |         static inline uint64_t _wyr4(const uint8_t *p) { uint32_t v; memcpy(&v, p, 4); return __builtin_bswap32(v);}
      |                                                                                 ^
/tmp/v_0/empty.11392888721645151286.tmp.c:439:32: note: expected ‘void *’ but argument is of type ‘const uint8_t *’ {aka ‘const unsigned char *’}
  439 | void *memcpy(void *dest, void *src, size_t n);
      |                          ~~~~~~^~~
/tmp/v_0/empty.11392888721645151286.tmp.c: In function ‘dlmalloc__Dlmalloc_init_top’:
/tmp/v_0/empty.11392888721645151286.tmp.c:7000:53: warning: passing argument 1 of ‘dlmalloc__align_offset_usize’ makes integer from pointer without a cast [-Wint-conversion]
 7000 |         usize offset = dlmalloc__align_offset_usize(dlmalloc__Chunk_to_mem(ptr));
      |                                                     ^~~~~~~~~~~~~~~~~~~~~~~~~~~
      |                                                     |
      |                                                     voidptr {aka void *}
/tmp/v_0/empty.11392888721645151286.tmp.c:6213:42: note: expected ‘usize’ {aka ‘long unsigned int’} but argument is of type ‘voidptr’ {aka ‘void *’}
 6213 | usize dlmalloc__align_offset_usize(usize addr) {
      |                                    ~~~~~~^~~~
/tmp/v_0/empty.11392888721645151286.tmp.c: In function ‘dlmalloc__Dlmalloc_add_segment’:
/tmp/v_0/empty.11392888721645151286.tmp.c:7231:55: warning: passing argument 1 of ‘dlmalloc__align_offset_usize’ makes integer from pointer without a cast [-Wint-conversion]
 7231 |                 offset = dlmalloc__align_offset_usize(dlmalloc__Chunk_to_mem((((dlmalloc__Chunk*)(rawsp)))));
      |                                                       ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
      |                                                       |
      |                                                       voidptr {aka void *}
/tmp/v_0/empty.11392888721645151286.tmp.c:6213:42: note: expected ‘usize’ {aka ‘long unsigned int’} but argument is of type ‘voidptr’ {aka ‘void *’}
 6213 | usize dlmalloc__align_offset_usize(usize addr) {
      |                                    ~~~~~~^~~~
/tmp/v_0/empty.11392888721645151286.tmp.c:7251:41: warning: comparison between pointer and integer
 7251 |                         if (nextp->head < old_end) {
      |                                         ^
/tmp/v_0/empty.11392888721645151286.tmp.c: In function ‘sys_execve’:
/tmp/v_0/empty.11392888721645151286.tmp.c:14189:61: warning: passing argument 3 of ‘sys_call3’ makes integer from pointer without a cast [-Wint-conversion]
14189 |         return ((int)(sys_call3(59U, ((u64)(filename)), argv.data, envp.data)));
      |                                                         ~~~~^~~~~
      |                                                             |
      |                                                             voidptr {aka void *}
/tmp/v_0/empty.11392888721645151286.tmp.c:2119:38: note: expected ‘u64’ {aka ‘long unsigned int’} but argument is of type ‘voidptr’ {aka ‘void *’}
 2119 | u64 sys_call3(u64 scn, u64 arg1, u64 arg2, u64 arg3);
      |                                  ~~~~^~~~
/tmp/v_0/empty.11392888721645151286.tmp.c:14189:72: warning: passing argument 4 of ‘sys_call3’ makes integer from pointer without a cast [-Wint-conversion]
14189 |         return ((int)(sys_call3(59U, ((u64)(filename)), argv.data, envp.data)));
      |                                                                    ~~~~^~~~~
      |                                                                        |
      |                                                                        voidptr {aka void *}
/tmp/v_0/empty.11392888721645151286.tmp.c:2119:48: note: expected ‘u64’ {aka ‘long unsigned int’} but argument is of type ‘voidptr’ {aka ‘void *’}
 2119 | u64 sys_call3(u64 scn, u64 arg1, u64 arg2, u64 arg3);
      |                                            ~~~~^~~~
/usr/bin/ld: /tmp/ccVPI9r3.ltrans0.ltrans.o: in function `_start':
<artificial>:(.text+0x1): undefined reference to `main'
collect2: error: ld returned 1 exit status
builder error: 
==================
C error. This should never happen.

This is a compiler bug, please report it using `v bug file.v`.

https://github.com/vlang/v/issues/new/choose

You can also use #help on Discord: https://discord.gg/vlang

[I] root@trump ~/v (master) [1]
#