Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

cgen: cast 'vstring'.str to char * if it is the expected type #19827

Closed
wants to merge 6 commits into from

Conversation

ttytm
Copy link
Member

@ttytm ttytm commented Nov 10, 2023

Should resolve: #19623

// v_with_c.v
fn foo(str &char) {
	println(unsafe { str.vstring() })
}

fn main() {
	foo('bar'.str)
}

Fixs a warning in the generated C code that results in a compiler error when compiling with gcc / clang and -cstrict

v -cc gcc -cstrict v_with_c.v
/tmp/v_1000/v_with_c_proj.9407719984852147895.tmp.c:12638:31: error: pointer targets in passing argument 1 of ‘main__foo’ differ in signedness [-Werror=pointe
r-sign]
12638 |         main__foo(_SLIT("bar").str);
      |                               ^
      |                               |
      |                               u8 * {aka unsigned char *}
/tmp/v_1000/v_with_c_proj.9407719984852147895.tmp.c:12633:38: note: expected ‘char *’ but argument is of type ‘u8 *’ {aka ‘unsigned char *’}
12633 | VV_LOCAL_SYMBOL void main__foo(char* str) {

E.g. clang
Screenshot_20231110_054138

Screenshot_20231110_054146

🤖 Generated by Copilot at f549d3e

Fix C code generation for function arguments with different pointer types. Add a test case c_fn_args.vv and its output file c_fn_args.out to verify the fix.

🤖 Generated by Copilot at f549d3e

  • Fix C code generation for function arguments with different pointer types (link)
  • Add a test case for C code generation for function arguments (link, link)

@ttytm ttytm changed the title cgen: cast 'vstring'.str to char * cgen: cast 'vstring'.str to char * Nov 10, 2023
@ttytm ttytm changed the title cgen: cast 'vstring'.str to char * cgen: cast 'vstring'.str to char * if it is the expected type Nov 10, 2023
@ttytm ttytm marked this pull request as draft November 10, 2023 06:05
@ttytm ttytm force-pushed the cgen/cast-to-exp-ptr-type branch from 7070120 to 8561ee9 Compare November 10, 2023 06:25
@ttytm ttytm force-pushed the cgen/cast-to-exp-ptr-type branch from 8561ee9 to 4eae9c6 Compare November 10, 2023 06:34
Comment on lines -166 to +167
fn C.mbedtls_x509_crt_parse(&C.mbedtls_x509_crt, &char, int) int
fn C.mbedtls_pk_parse_key(&C.mbedtls_pk_context, &char, int, &char, int, voidptr, voidptr) int
fn C.mbedtls_x509_crt_parse(&C.mbedtls_x509_crt, &u8, int) int
fn C.mbedtls_pk_parse_key(&C.mbedtls_pk_context, &u8, int, &char, int, voidptr, voidptr) int
Copy link
Member Author

@ttytm ttytm Nov 10, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixing this to use &u8 where types are declared as unsigned char * allows for green CI.

int mbedtls_pk_parse_key( mbedtls_pk_context *ctx,
const unsigned char *key, size_t keylen,
const unsigned char *pwd, size_t pwdlen,

not strictly required, but a little but more correct and expressive and
makes things uniform within the module.
@ttytm ttytm marked this pull request as ready for review November 10, 2023 10:02
Comment on lines +1 to +9
fn foo(bar &char, baz &char) {
println(unsafe { bar.vstring() + baz.vstring() })
}

fn main() {
baz := 'baz'.str
// Should compile with `-cc [gcc/clang] -cstrict`
foo('bar'.str, baz)
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No, it should not compile with -cstrict. Such hacks are brittle.

It should need explicit casts, like this: foo(&char('bar'.str), &char(baz)).

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

but why should the user do the explicit cast with flags? I feel they don't need to do it.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am not sure what flags are you talking about?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

-cstrict

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The user should have done the explicit cast in all cases, -cstrict just makes the C compilers that support it much more strict, and makes them produce more errors for situations, that normally they accept.

Copy link
Member

@spytheman spytheman left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Imho the changes to mbedtls.c.v are good, since they reflect the C API more closely. The rest should be reverted.

@spytheman spytheman closed this Nov 10, 2023
@ttytm
Copy link
Member Author

ttytm commented Nov 10, 2023

Wasn't it just a proper cast of an alias type?

pub type char = u8

// char is an alias for u8 and is equivalent to u8 in all ways.

It was limited to both type kinds and only if the expected type is char.

@medvednikov
Copy link
Member

medvednikov commented Nov 10, 2023

@spytheman don't be too hasty with closing PRs if there are still things to discuss.

According to the C standard, char can be u8 or i8:

The standard does not specify if plain char is signed or unsigned

V's string.str is &u8, so technically it can't be safely autocast.

But I do think it's a good idea to make it compatible. Though string.str is not guaranteed to end with a /0

@JalonSolov
Copy link
Contributor

It is certainly documented that way, in the V source...

	str &u8 = 0 // points to a C style 0 terminated string of bytes.

@spytheman
Copy link
Member

V's string.str is &u8, so technically it can't be safely autocast.

Yes, that is exactly the problem, it needs a cast that is better to be explicit, just like all other similar casts. Adding code that will auto cast it, in only specific situations (only for s.str), makes writing wrappers harder, by not allowing the programmer to build a clear mental model on what is needed, in what situations.

@spytheman
Copy link
Member

It seems nice on the surface, until you get accustomed to it, and then bites you in unexpected places (in any custom struct for example, that has a &u8 field), where it will also look like it should work, but it does not, and you do not know why, and why it did in the other place, without having to read the compiler source.

@spytheman
Copy link
Member

// char is an alias for u8 and is equivalent to u8 in all ways.

I can not find where is that cited from? char is not an alias to u8, char is a separate type, and is exactly equivalent to the C char, and it is intended to be used only for interoperability with C (for casts in wrappers).

@ttytm
Copy link
Member Author

ttytm commented Nov 10, 2023

// char is an alias for u8 and is equivalent to u8 in all ways.

I can not find where is that cited from? char is not an alias to u8, char is a separate type, and is exactly equivalent to the C char, and it is intended to be used only for interoperability with C (for casts in wrappers).

It's in the hover dialog of &char

@ttytm
Copy link
Member Author

ttytm commented Nov 10, 2023

I think it's totally fine to do the manual cast. When it comes to functions that do some form of interop, it can make things even more explicit and might help to better understand the current context when reading code.

The main drive here was to make it work the same with and without -cstrict.

Adding code that will auto cast it, in only specific situations (only for s.str),

The PR title is badly chosen, it was intended to cast &u8's to &char when &char is the expected type.

@spytheman
Copy link
Member

It's in the hover dialog of &char

What hover dialog? It is not found anywhere in the V repo. That information is plain wrong, and needs to be fixed.

@spytheman
Copy link
Member

The PR title is badly chosen, it was intended to cast &u8's to &char when &char is the expected type.

I am not talking about the title, the new code in cgen is specifically special casing stuff:

+		} else if expr !is ast.StringLiteral && exp_sym.kind == .char && got_sym.kind == .u8 {
+			g.write('(${exp_styp})')
+		}

@ttytm
Copy link
Member Author

ttytm commented Nov 11, 2023

It's in the hover dialog of &char

What hover dialog? It is not found anywhere in the V repo. That information is plain wrong, and needs to be fixed.

Might be v-analyzer then.
vscode:
Screenshot_20231111_010145
neovim:
Screenshot_20231111_010056

Edit:
It comes from v-analyzer
https://github.com/v-analyzer/v-analyzer/blob/099a85fbd34a8b9db9f83da19f184bd4c1886e7c/metadata/stubs/primitives.v#L68C18-L68C18

@JalonSolov
Copy link
Contributor

There is no module named stubs in V...

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Proper cast of <string>.str to char *
5 participants