Skip to content

Consecutive asserts ignore previous #24194

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

Open
einar-hjortdal opened this issue Apr 12, 2025 · 6 comments
Open

Consecutive asserts ignore previous #24194

einar-hjortdal opened this issue Apr 12, 2025 · 6 comments
Labels
Feature/Enhancement Request This issue is made to request a feature or an enhancement to an existing one.

Comments

@einar-hjortdal
Copy link
Contributor

einar-hjortdal commented Apr 12, 2025

Describe the bug

An assert should be able to refine a type. But it doesn't seem to do it

Reproduction Steps

module main

interface Val {}

const s = 'cool'

fn get_val() Val {
	return s
}

fn main() {
	v := get_val()
	assert v is string // is pass then v is string, otherwise errors.
	assert v == s // should work because assert above passes.
	// error: infix expr: cannot use `string` (right expression) as `Val`
}

Expected Behavior

Second assert should not cause errors

Current Behavior

error: infix expr: cannot use string (right expression) as Val

Possible Solution

No response

Additional Information/Context

No response

V version

V 0.4.10 c2e27ef

Environment details (OS name and version, etc.)

|V full version      |V 0.4.10 537605a.c2e27ef
|:-------------------|:-------------------
|OS                  |linux, "openmamba release 2024.6 for x86_64 (rolling)"
|Processor           |8 cpus, 64bit, little endian, Intel(R) Core(TM) i7-4790K CPU @ 4.00GHz
|Memory              |0.41GB/31.16GB
|                    |
|V executable        |/home/einar/.local/lib64/v/v
|V last modified time|2025-04-12 07:08:24
|                    |
|V home dir          |OK, value: /home/einar/.local/lib64/v
|VMODULES            |OK, value: /home/einar/.vmodules
|VTMP                |OK, value: /tmp/v_1000
|Current working dir |OK, value: /home/einar/Documents/projects/vlang/active/redict
|                    |
|Git version         |git version 2.49.0
|V git status        |weekly.2025.14-35-gc2e27ef2
|.git/config present |true
|                    |
|cc version          |cc (GCC) 14.2.1 20240909
|gcc version         |gcc (GCC) 14.2.1 20240909
|clang version       |N/A
|tcc version         |tcc version 0.9.28rc 2024-07-31 HEAD@1cee0908 (x86_64 Linux)
|tcc git status      |thirdparty-linux-amd64 0134e9b9
|emcc version        |N/A
|glibc version       |ldd (GNU libc) 2.41

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.

@einar-hjortdal einar-hjortdal added the Bug This tag is applied to issues which reports bugs. label Apr 12, 2025
@einar-hjortdal
Copy link
Contributor Author

assert v is string && v == s works however it's limited:
If after the assert I wanted to return v as a string value in a function that returns !string, this would not be possible.

@felipensp felipensp added Feature/Enhancement Request This issue is made to request a feature or an enhancement to an existing one. and removed Bug This tag is applied to issues which reports bugs. labels Apr 12, 2025
@JalonSolov
Copy link
Contributor

The first assert has it's own context, where v is resolved.

The 2nd assert is a new context, where it is not resolved.

This is why the combined assert works - it is resolved by the first condition, and retained for the 2nd because it's the same context.

@jorgeluismireles
Copy link

I think you should unwrap v until is a real string. This works:

fn main() {
	v := get_val()
	match v {
		string  {
			assert v == s
			// assert v is string -> error: `is` can only be used with interfaces and sum types
		}
		else { }
	}
}

Once unwrapped assert v is string can't be compiled.

@Delta456
Copy link
Member

Doing it explicitly is better than doing it implicitly, as @jorgeluismireles says

@einar-hjortdal
Copy link
Contributor Author

einar-hjortdal commented Apr 15, 2025

I think you should unwrap v until is a real string. This works:

fn main() {
v := get_val()
match v {
string {
assert v == s
// assert v is string -> error: is can only be used with interfaces and sum types
}
else { }
}
}
Once unwrapped assert v is string can't be compiled.

Well, I am doing that, but imo both the following should work. If match is the only correct strategy in V maybe the compiler should tell the user.

fn main() {
	v := get_val()
	assert v is string 
	assert v == s 
}
fn main() {
	v := get_val()
	if v !is string {
		panic(error('v is not a string'))
	}
	if v == s {
		println('do something')
	}
}

@JalonSolov
Copy link
Contributor

JalonSolov commented Apr 15, 2025

It has nothing to do with match, it is the context. The block. Whatever you wish to call it. Once something is resolved in a block, it stays resolved in a block.

The only thing that isn't obvious is that assert creates it's own block, since it doesn't require using { and } around the check.

Even if it appears that it should be resolved after the first assert, it isn't... it's only resolved within the assert block.

This is very much the same as creating a variable inside an if or for, etc. That variable is not available outside of that block.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Feature/Enhancement Request This issue is made to request a feature or an enhancement to an existing one.
Projects
None yet
Development

No branches or pull requests

5 participants