From 2c65cea97f29b5c10bb7e8f85d532671b7a69430 Mon Sep 17 00:00:00 2001 From: TermiNexus Date: Tue, 3 Nov 2026 19:49:04 +0800 Subject: [PATCH] feat(lsp): parse own/parenthesized/nullable types and report implicit conversions ParseType now understands `own T`, `(T)` and `T?`, so assignment checks reuse the existing int/pointer conversion codes (integer<->pointer, pointer mismatch). Value-typed targets reject pointer values, and duplicate nullable reporting is gone. --- App/lib/lsp/LspChecks.vp | 18 +++++++++++------- App/lib/lsp/LspTypes.vp | 25 +++++++++++++++++++++++++ 2 files changed, 36 insertions(+), 7 deletions(-) diff --git a/App/lib/lsp/LspChecks.vp b/App/lib/lsp/LspChecks.vp index 5890129..983d50f 100644 --- a/App/lib/lsp/LspChecks.vp +++ b/App/lib/lsp/LspChecks.vp @@ -1570,14 +1570,16 @@ def _WalkChecks(pool: memhub.MemManager*, doc: LspDocuments.LspDocument*, ann: str = _DeclAnnotationText(pool, doc, an.target) dst: LspTypes.LspType* = LspTypes.ParseType(pool, ann) _CheckAssignValue(pool, doc, a, ws, table_pool, dst, an.value, None, errors) - if _NullableDetail(ann) == 0 \ + if an.value is not None and _AnnIsPointerLike(pool, doc, an) != 0 \ + and _NullableDetail(ann) == 0 \ and _NullableDetail(_SymDetailAt(pool, a, an.value)) != 0: _EmitTypeError(pool, doc, an.value, "a nullable value may be None; declare the target as `T?` or unwrap it with `match` first", errors) _WalkChecksExprs(pool, doc, a, ws, table_pool, an.value, errors) if an.value is not None and _AnnIsPointerLike(pool, doc, an) == 0 \ - and _NullableDetail(_SymDetailAt(pool, a, an.value)) == 0 \ - and _ValueIsPointer(pool, a, an.value) != 0: + and _ValueIsPointer(pool, a, an.value) != 0 \ + and (an.value.kind() == ast.ASTKind.New or dst is None + or dst.kind == LspTypes.LT_AGG or dst.kind == LspTypes.LT_UNKNOWN): _EmitTypeError(pool, doc, an.value, "a pointer/`own` value cannot be stored into a value-typed variable; declare the target as `own T` / `T*`, or dereference explicitly (`*p`)", errors) if an.target is not None and an.target.kind() == ast.ASTKind.Name: @@ -1604,15 +1606,17 @@ def _WalkChecks(pool: memhub.MemManager*, doc: LspDocuments.LspDocument*, continue dst_t: LspTypes.LspType* = _TypeFromDetail(pool, tt.detail) _CheckAssignValue(pool, doc, a, ws, table_pool, dst_t, asn.value, None, errors) - if _NullableDetail(tt.detail) == 0 \ + if _DetailIsPointer(tt.detail) != 0 and _NullableDetail(tt.detail) == 0 \ and _NullableDetail(_SymDetailAt(pool, a, asn.value)) != 0: _EmitTypeError(pool, doc, asn.value, "a nullable value may be None; declare the target as `T?` or unwrap it with `match` first", errors) if tt.detail is not None and _DetailIsPointer(tt.detail) == 0 \ - and _NullableDetail(_SymDetailAt(pool, a, asn.value)) == 0 \ and _ValueIsPointer(pool, a, asn.value) != 0: - _EmitTypeError(pool, doc, asn.value, - "a pointer/`own` value cannot be stored into a value-typed variable; declare the target as `own T` / `T*`, or dereference explicitly (`*p`)", errors) + dst_a: LspTypes.LspType* = _TypeFromDetail(pool, tt.detail) + if asn.value.kind() == ast.ASTKind.New or dst_a is None \ + or dst_a.kind == LspTypes.LT_AGG or dst_a.kind == LspTypes.LT_UNKNOWN: + _EmitTypeError(pool, doc, asn.value, + "a pointer/`own` value cannot be stored into a value-typed variable; declare the target as `own T` / `T*`, or dereference explicitly (`*p`)", errors) _WalkChecksExprs(pool, doc, a, ws, table_pool, asn.value, errors) if asn.value is not None and asn.value.kind() == ast.ASTKind.Name: sn_a: ast.Name* = ast.Name*(asn.value) diff --git a/App/lib/lsp/LspTypes.vp b/App/lib/lsp/LspTypes.vp index 36c0bd0..8397105 100644 --- a/App/lib/lsp/LspTypes.vp +++ b/App/lib/lsp/LspTypes.vp @@ -243,6 +243,31 @@ def ParseType(pool: memhub.MemManager*, text: str) -> LspType*: if p is not None: p.pointee = inner return p + # Parenthesized type group: `( T )` (suffixes such as `?` are handled below/outside). + w_len0: sizet = string.strlen(working) + if w_len0 >= 2 and working[0] == '(' and working[w_len0 - 1] == ')': + return ParseType(pool, _Trim(pool, _Slice(pool, working, 1, int(w_len0 - 1)))) + # Ownership prefix: `own T` (one owned pointer layer; an existing pointer keeps its layer). + if _Starts(working, "own ") != 0: + rest_own: str = _Trim(pool, _Slice(pool, working, 4, int(string.strlen(working)))) + inner_own: LspType* = ParseType(pool, rest_own) + if inner_own is not None and inner_own.kind == LT_PTR: + return inner_own + p_own: LspType* = _NewType(pool, LT_PTR) + if p_own is not None: + p_own.pointee = inner_own + return p_own + # Nullable suffix: `T?` (pointer niche; an existing pointer keeps its layer). + n_q: sizet = string.strlen(working) + if n_q > 0 and working[n_q - 1] == '?': + base_q: str = _Trim(pool, _Slice(pool, working, 0, int(n_q - 1))) + inner_q: LspType* = ParseType(pool, base_q) + if inner_q is not None and inner_q.kind == LT_PTR: + return inner_q + p_q: LspType* = _NewType(pool, LT_PTR) + if p_q is not None: + p_q.pointee = inner_q + return p_q # Leading array: []T / [N]T. if string.strlen(working) > 0 and working[0] == '[': close: int = -1