-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathSema.h
More file actions
155 lines (133 loc) · 6.99 KB
/
Copy pathSema.h
File metadata and controls
155 lines (133 loc) · 6.99 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
#ifndef BLANG_SEMA_H_
#define BLANG_SEMA_H_
#include <string>
#include <set>
#include <map>
#include "Type.h"
#include "Expression.h"
#include "DiagnosticEngine.h"
namespace QLang
{
// The semantic-analysis pass (U3). Runs between Module::Parse and code
// generation, in ALL build configurations (no BLANG_HAS_LLVM guard) so
// --parse-only becomes "parse + sema". It walks a parsed module's AST,
// resolves struct field/method references against the base expression's
// resolved type, annotates every determinable expression with its resolved
// type (the single shared typed-AST representation codegen reads), and
// reports located errors through the U2 DiagnosticEngine.
//
// Scope split (research R2): the parser already resolves variables and
// functions eagerly, throwing located CompileErrors before sema runs. Sema
// owns member (field/method) resolution and type annotation only; it never
// re-reports the parser's var/func errors (FR-009). It adds no type-CHECKING
// rule (arity/compat/coercion/ownership/concurrency) — those are U4–U7.
class Sema
{
public:
// Analyze one parsed, non-extern module. Returns true iff no semantic
// diagnostic was reported (the driver skips codegen and exits non-zero
// on false). Extern .bmod modules are not analyzed (contracts/sema-pass).
static bool analyze( Module *module, Scope *scope, DiagnosticEngine &diag );
private:
Sema( Scope *scope, DiagnosticEngine &diag, const std::string &moduleId = "" )
: mScope( scope ), mDiag( diag ), mModuleId( moduleId ) {}
// U6b-3 (DC9/KI-23): identity (source-file basename) of the module being
// analyzed — the USE-SITE module. A field access on a struct whose defining
// module differs is a private-field reach-in across a module boundary.
std::string mModuleId;
void visitFunction( FunctionDefinition *func );
void visitStruct( StructDefinition *structDef );
// A declaration without a body is an INTERFACE form: it is what a .bmod
// carries so a consumer can resolve an imported type's constructor and
// methods. In ordinary source it would silently codegen an empty
// function returning zero, so it is rejected here, located, in all build
// modes. Protocol requirements are bodyless by design and never reach
// this check.
void checkBodylessMember( FunctionDefinition *func, const std::string &ownerName );
// The compiler reserves the "__" symbol family for names it synthesizes
// (__<Struct>_dtor, __<Struct>_new, __enum_<Name>_box_dtor, ...). A
// source declaration that mangles into that family could collide with
// one of them, so it is rejected. `extern fn` is exempt: it names a
// foreign C symbol rather than creating a BLang one.
void checkReservedName( const std::string &name, const SourceLocation &loc,
const std::string &kind );
// P9 (design record): an exported declaration may only reference
// exported types. Enforced at the LIBRARY build, located at the
// offending declaration.
//
// Without this the emitter writes a reference to a type it never
// declares, the library exits 0, and the failure lands on the CONSUMER
// as a syntax error inside a generated file they never wrote. That is
// the wrong error, in the wrong file, at the wrong time, shown to the
// wrong person.
//
// `what` names the offending position for the diagnostic
// ("parameter of pub fn 'x'", "field of @json struct 'T'", ...).
void checkExportedTypeRef( const Type *type, const SourceLocation &loc,
const std::string &what );
void checkExportedSignature( FunctionDefinition *func,
const std::string &what );
bool isExportedDataContract( StructDefinition *structDef ) const;
void visitStmt( Statement *stmt );
// Resolve/annotate an expression bottom-up. Returns its resolved Type
// (best-effort, from resolution only), or nullptr when the type is not
// determinable in this unit. Also stamps the node via setResolvedType.
Type *visitExpr( Expression *expr );
// Member resolution helpers.
void resolveFieldAccess( FieldAccessExpression *fa, Type *baseType );
void resolveMethodCall( MethodCallExpression *mc, Type *baseType );
// Database query/insert/update/delete field validation (located, all
// build modes). tableStructFor resolves the `table struct` (located error
// for unknown/non-table); checkTableField reports an unknown column;
// validateTableSteps walks where/order/set pipeline steps.
// Builtin to_json(value): require a @json-annotated struct argument
// (located error, all build modes).
void validateToJsonArg( CallExpression *call );
StructDefinition *tableStructFor( const std::string &tableName, Expression *node );
void checkTableField( StructDefinition *table, const std::string &field,
const SourceLocation &loc );
void collectQueryFieldExprs( const Expression *e,
std::vector<const QueryFieldExpression *> &out );
void validateTableSteps( const std::string &tableName,
const std::vector<QueryPipelineStep> &steps, Expression *node );
// The concrete user struct a base type names, or nullptr when the base
// is a builtin (string/Array/Buffer/…), a generic parameter, an enum, an
// imported/namespaced symbol we cannot see, or otherwise not a concrete
// struct in scope. A nullptr result means "leave unchecked" (FR-008/R5):
// sema never fabricates an "unknown member" error on such bases.
StructDefinition *structForType( Type *baseType );
// U4 type-compatibility (members: use mScope to classify a type).
bool isCheckableType( Type *t );
bool typesCompatible( Type *from, Type *to );
// U5 match/generics helpers.
EnumDefinition *enumForType( Type *t );
void checkConstraint( Type *arg, const std::string &constraint,
const std::string ¶mName, const SourceLocation &loc );
// U7 spawn-capture helpers.
bool isHeapType( Type *t );
void collectSpawnRefs( Statement *stmt,
std::set<VariableDefinition*> &refs,
std::set<VariableDefinition*> &locals );
// U1 (diagnostics): unused-local-variable lint, per function. Collect
// declared locals and every referenced/assigned variable name; warn on a
// local never mentioned again. Name-based and conservative — a name used
// anywhere in the function suppresses the warning, so it only ever misses
// warnings, never false-positives a genuinely used variable.
std::vector<VariableDefinition*> mLocalDecls;
std::set<std::string> mReferencedNames;
Scope *mScope;
DiagnosticEngine &mDiag;
bool mReported = false; // any sema diagnostic emitted this run
FunctionDefinition *mCurrentFunc = nullptr; // U4: enclosing fn for return checks
// U6 ownership/move analysis (bounded flow analysis, per function).
std::set<VariableDefinition*> mMoved;
std::map<VariableDefinition*, int> mDeclLoopDepth;
std::map<VariableDefinition*, int> mDeclSpawnDepth;
int mLoopDepth = 0;
int mSpawnDepth = 0;
// Owns the synthesized `int` type assigned to range-loop variables
// (setVariableType stores a SmartPtr, but keep an owner for clarity).
SmartPtr<Type> mIntType;
};
} // namespace QLang
#endif // BLANG_SEMA_H_