fix(semantic): reject duplicate identifiers across const and var sections
A var declaration shadowing a same-block const (or a const declared twice in the same block) was silently accepted instead of raising a Duplicate identifier error. Root cause: AnalyseBlock registers consts in the outer scope then pushes a new inner scope before calling AnalyseVarDecls, so FTable.Define (which only checks the current scope) could not detect the clash. Fix: AnalyseVarDecls now scans the block's own ConstDecls list before defining each var name. AnalyseConstDecls now treats a duplicate as an error when the collision originates from an earlier const in the same block; cross-unit const shadowing (e.g. a unit redefining a system.pas constant) continues to be silently accepted. Also removes the redundant LineEnding const from blaise.testing.pas — it is already exported by system.pas. Fixes: https://github.com/graemeg/blaise/issues/30
This commit is contained in:
parent
c17a8b027c
commit
08beb33689
|
|
@ -38,7 +38,7 @@ https://github.com/graemeg/opdebugger[OPDF] debug format support.
|
|||
== 🚀 Project Status
|
||||
|
||||
* **Self-Hosting:** Yes. Blaise bootstraps and recompiles itself with byte-for-byte fixpoint. FPC is no longer required — the entire toolchain runs on Blaise alone.
|
||||
* **Testing:** 1846 tests and growing (Test-Driven Development from day one). The test suite itself compiles under Blaise.
|
||||
* **Testing:** 1934 tests and growing (Test-Driven Development from day one). The test suite itself compiles under Blaise.
|
||||
* **Backends:** Currently utilising a QBE backend, with an LLVM backend in active development.
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -36,9 +36,6 @@ interface
|
|||
uses
|
||||
Classes, SysUtils;
|
||||
|
||||
const
|
||||
LineEnding = #10;
|
||||
|
||||
type
|
||||
{ TRunMethod — type of a parameter-less method on any TObject descendant.
|
||||
Used as the cast target for the published-method dispatch trampoline:
|
||||
|
|
|
|||
|
|
@ -1881,6 +1881,7 @@ var
|
|||
RefSym: TSymbol;
|
||||
TD: TTypeDesc;
|
||||
Resolved: string;
|
||||
IsSameBlockDup: Boolean;
|
||||
begin
|
||||
for I := 0 to ABlock.ConstDecls.Count - 1 do
|
||||
begin
|
||||
|
|
@ -1927,7 +1928,21 @@ begin
|
|||
Sym.ConstValue := CD.IntVal;
|
||||
Sym.ConstString := CD.StrVal;
|
||||
if not FTable.Define(Sym) then
|
||||
Sym.Free; { duplicate const — silently ignore }
|
||||
begin
|
||||
Sym.Free;
|
||||
{ Only error for same-block duplicates. Cross-unit const shadowing
|
||||
(e.g. a unit redefining a system.pas constant) is silently accepted,
|
||||
matching FPC behaviour and preserving the existing test coverage. }
|
||||
IsSameBlockDup := False;
|
||||
for J := 0 to I - 1 do
|
||||
if SameText(TConstDecl(ABlock.ConstDecls.Items[J]).Name, CD.Name) then
|
||||
begin
|
||||
IsSameBlockDup := True;
|
||||
Break;
|
||||
end;
|
||||
if IsSameBlockDup then
|
||||
SemanticError(Format('Duplicate identifier ''%s''', [CD.Name]), CD.Line, CD.Col);
|
||||
end;
|
||||
end;
|
||||
end;
|
||||
|
||||
|
|
@ -3370,7 +3385,7 @@ end;
|
|||
|
||||
procedure TSemanticAnalyser.AnalyseVarDecls(ABlock: TBlock);
|
||||
var
|
||||
I, J: Integer;
|
||||
I, J, K: Integer;
|
||||
Decl: TVarDecl;
|
||||
Typ: TTypeDesc;
|
||||
VarName: string;
|
||||
|
|
@ -3407,6 +3422,16 @@ begin
|
|||
for J := 0 to Decl.Names.Count - 1 do
|
||||
begin
|
||||
VarName := Decl.Names.Strings[J];
|
||||
{ Consts from the same block live in the immediately enclosing scope
|
||||
(AnalyseBlock pushes a new scope before calling AnalyseVarDecls, so
|
||||
FTable.Define cannot see same-block consts). Scan the block's own
|
||||
ConstDecls list so we catch only same-block clashes, not legitimate
|
||||
shadowing of outer-scope or unit-imported consts. }
|
||||
for K := 0 to ABlock.ConstDecls.Count - 1 do
|
||||
if SameText(TConstDecl(ABlock.ConstDecls.Items[K]).Name, VarName) then
|
||||
SemanticError(
|
||||
Format('Duplicate identifier ''%s''', [VarName]),
|
||||
Decl.Line, Decl.Col);
|
||||
Sym := TSymbol.Create(VarName, skVariable, Typ);
|
||||
Sym.IsWeak := Decl.IsWeak;
|
||||
Sym.IsGlobal := Decl.IsGlobal;
|
||||
|
|
|
|||
|
|
@ -29,6 +29,10 @@ type
|
|||
procedure TestVarDecl_MultiName_BothRegistered;
|
||||
procedure TestVarDecl_UnknownType_RaisesError;
|
||||
procedure TestVarDecl_Duplicate_RaisesError;
|
||||
{ Const then var with same name in same block is a duplicate }
|
||||
procedure TestVarDecl_DuplicatesConst_RaisesError;
|
||||
{ Const redeclared with same name in same block is a duplicate }
|
||||
procedure TestConst_Duplicate_RaisesError;
|
||||
|
||||
{ Expression type inference }
|
||||
procedure TestExpr_IntLiteral_TypeIsInteger;
|
||||
|
|
@ -169,6 +173,18 @@ begin
|
|||
'program P; var x: Integer; x: string; begin end.');
|
||||
end;
|
||||
|
||||
procedure TSemanticTests.TestVarDecl_DuplicatesConst_RaisesError;
|
||||
begin
|
||||
AnalyseExpectError(
|
||||
'program P; const b = 42; var b: Integer; begin end.');
|
||||
end;
|
||||
|
||||
procedure TSemanticTests.TestConst_Duplicate_RaisesError;
|
||||
begin
|
||||
AnalyseExpectError(
|
||||
'program P; const b = 42; b = 99; begin end.');
|
||||
end;
|
||||
|
||||
{ ------------------------------------------------------------------ }
|
||||
{ Expression type inference }
|
||||
{ ------------------------------------------------------------------ }
|
||||
|
|
|
|||
Loading…
Reference in a new issue