-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathCGStruct.cpp
More file actions
2659 lines (2387 loc) · 95.4 KB
/
Copy pathCGStruct.cpp
File metadata and controls
2659 lines (2387 loc) · 95.4 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
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
#include "CodeGen.h"
#include "llvm/IR/DerivedTypes.h"
#include "llvm/IR/Constants.h"
#include <iostream>
using namespace QLang;
using namespace std;
Type *CodeGen::resolveVariantPayloadType( Type *assocType, EnumDefinition *enumDef,
Type *concreteEnumType )
{
if ( assocType == nullptr || enumDef == nullptr || concreteEnumType == nullptr )
return assocType;
const auto &gps = enumDef->getGenericParams();
for ( size_t i = 0; i < gps.size(); i++ )
{
if ( gps[i].mName == assocType->getName() &&
(int)i < concreteEnumType->getNumTypeParams() )
return concreteEnumType->getTypeParam( (int)i );
}
return assocType;
}
void CodeGen::emitEnumPayloadRelease( llvm::AllocaInst *alloca, EnumDefinition *enumDef,
Type *concreteEnumType )
{
if ( enumDef == nullptr )
return;
llvm::StructType *enumType = getOrCreateEnumType( enumDef );
// Load the enum value from the alloca into a scratch slot, then release
// its payloads through the shared walk.
llvm::Value *enumVal = mBuilder->CreateLoad( enumType, alloca, "enum.cleanup" );
llvm::AllocaInst *enumTmp = mBuilder->CreateAlloca( enumType, nullptr, "enum.cleanup.tmp" );
mBuilder->CreateStore( enumVal, enumTmp );
emitEnumPayloadReleaseFromPtr( enumTmp, enumDef, concreteEnumType );
}
bool CodeGen::enumHasRefcountedPayload( EnumDefinition *enumDef, Type *concreteEnumType )
{
if ( enumDef == nullptr )
return false;
for ( auto &variant : enumDef->mVariants )
{
for ( auto &at : variant.mAssociatedTypes )
{
string atn = resolveVariantPayloadType(
(Type *)at, enumDef, concreteEnumType )->getName();
if ( atn == "string" || atn == "Array" || atn == "Buffer" ||
isUserStructType( atn ) || mEnumDefMap.count( atn ) != 0 )
return true;
}
}
return false;
}
void CodeGen::emitEnumPayloadReleaseFromPtr( llvm::Value *enumPtr,
EnumDefinition *enumDef, Type *concreteEnumType )
{
if ( enumDef == nullptr )
return;
llvm::StructType *enumType = getOrCreateEnumType( enumDef );
llvm::Type *ptrType = llvm::PointerType::get( *mContext, 0 );
llvm::DataLayout dl( mModule.get() );
llvm::Type *payloadArrType = enumType->getElementType( 1 );
// Load the tag
llvm::Value *tagPtr = mBuilder->CreateStructGEP( enumType, enumPtr, 0, "enum.cleanup.tag.ptr" );
llvm::Value *tag = mBuilder->CreateLoad(
llvm::Type::getInt32Ty( *mContext ), tagPtr, "enum.cleanup.tag" );
// Create basic blocks for the switch
llvm::Function *func = mBuilder->GetInsertBlock()->getParent();
llvm::BasicBlock *mergeBB = llvm::BasicBlock::Create( *mContext, "enum.cleanup.done", func );
// Build switch for variants with refcounted payloads
llvm::SwitchInst *sw = mBuilder->CreateSwitch( tag, mergeBB, enumDef->mVariants.size() );
for ( size_t vi = 0; vi < enumDef->mVariants.size(); vi++ )
{
auto &variant = enumDef->mVariants[vi];
bool hasRef = false;
for ( auto &at : variant.mAssociatedTypes )
{
string atn = resolveVariantPayloadType(
(Type *)at, enumDef, concreteEnumType )->getName();
if ( atn == "string" || atn == "Array" || atn == "Buffer" ||
isUserStructType( atn ) || mEnumDefMap.count( atn ) != 0 )
{
hasRef = true;
break;
}
}
if ( !hasRef )
continue;
llvm::BasicBlock *variantBB = llvm::BasicBlock::Create(
*mContext, "enum.cleanup." + variant.mName, func );
sw->addCase(
llvm::ConstantInt::get( llvm::Type::getInt32Ty( *mContext ), vi ),
variantBB );
mBuilder->SetInsertPoint( variantBB );
llvm::Value *payloadPtr = mBuilder->CreateStructGEP(
enumType, enumPtr, 1, "enum.cleanup.payload" );
// Release each refcounted payload at ITS byte offset. The walk must
// mirror construction: erased generic-param slots and boxed enums are
// pointer-sized; everything else advances by its LLVM alloc size.
// (Previously every release read byte 0 — wrong for any variant whose
// refcounted payload is not first, e.g. tag(int, string).)
uint64_t off = 0;
for ( auto &at : variant.mAssociatedTypes )
{
Type *resolved = resolveVariantPayloadType(
(Type *)at, enumDef, concreteEnumType );
string atn = resolved->getName();
bool isGenericSlot = false;
for ( auto &gp : enumDef->mGenericParams )
{
if ( gp.mName == ( (Type *)at )->getName() )
{
isGenericSlot = true;
break;
}
}
uint64_t slot;
if ( isGenericSlot || mEnumDefMap.count( atn ) != 0 )
slot = 8;
else
{
slot = dl.getTypeAllocSize( getLLVMType( resolved ) );
if ( slot == 0 ) slot = 4;
}
llvm::Value *bytePtr = mBuilder->CreateGEP(
payloadArrType, payloadPtr,
{ llvm::ConstantInt::get( llvm::Type::getInt64Ty( *mContext ), 0 ),
llvm::ConstantInt::get( llvm::Type::getInt64Ty( *mContext ), (int64_t)off ) },
"enum.cleanup.payload.byte" );
if ( atn == "string" )
{
llvm::Value *strVal = mBuilder->CreateLoad(
ptrType, bytePtr, "enum.cleanup.str" );
mBuilder->CreateCall( getOrDeclareStringRelease(), { strVal } );
}
else if ( atn == "Array" )
{
llvm::Value *arrVal = mBuilder->CreateLoad(
ptrType, bytePtr, "enum.cleanup.arr" );
mBuilder->CreateCall( getOrDeclareArrayRelease(), { arrVal } );
}
else if ( atn == "Buffer" )
{
llvm::Value *bufVal = mBuilder->CreateLoad(
ptrType, bytePtr, "enum.cleanup.buf" );
mBuilder->CreateCall( getOrDeclareBufferRelease(), { bufVal } );
}
else if ( isUserStructType( atn ) || mEnumDefMap.count( atn ) != 0 )
{
// Struct payload or boxed enum payload: both are rc pointers.
// Releasing a box runs its generated dtor, which recursively
// releases the boxed value's own payloads.
llvm::Value *rcVal = mBuilder->CreateLoad(
ptrType, bytePtr, "enum.cleanup.rc" );
mBuilder->CreateCall( getOrDeclareRcRelease(), { rcVal } );
}
off += slot;
}
mBuilder->CreateBr( mergeBB );
}
mBuilder->SetInsertPoint( mergeBB );
}
llvm::Function *CodeGen::getOrGenStructDestructor( StructDefinition *sd,
const std::map<std::string, std::string> &typeSub )
{
if ( sd == nullptr )
return nullptr;
// Determine the mangled name for this destructor (include type args for
// generics). For a GENERIC INSTANCE this must mirror mangleGenericName exactly
// — base + module-identity digest (U1/D10) + type args — so the dtor symbol is
// as distinct across modules as the type it destroys: two modules' Box<int>
// otherwise share __Box_int_dtor and the linker collapses them (P10) even
// though their types no longer collapse. Non-generic dtors keep the plain
// __Name_dtor form (the .bmod factory references it by that name, KI-5).
string dtorName = "__" + sd->getName();
if ( !typeSub.empty() )
{
if ( !sd->getModuleDigest().empty() )
dtorName += "_m" + sd->getModuleDigest();
for ( auto &gp : sd->mGenericParams )
{
auto it2 = typeSub.find( gp.mName );
if ( it2 != typeSub.end() )
dtorName += "_" + it2->second;
}
}
dtorName += "_dtor";
// Check cache
auto it = mStructDtorMap.find( dtorName );
if ( it != mStructDtorMap.end() )
return it->second;
// Check if the struct has any refcounted fields that need cleanup
// For generic structs, look up the mangled instantiation name (e.g., "Box_string")
llvm::StructType *structType = nullptr;
string structTypeName = sd->getName();
if ( !typeSub.empty() )
{
// Mirror mangleGenericName exactly (base + U1 module-identity digest + type
// args) so this lookup finds the instance type registered under the
// digested name (mStructTypeMap, CGTypes.cpp). Without the digest the lookup
// misses and the dtor runs against the wrong (unsubstituted) layout —
// releasing garbage field pointers.
if ( !sd->getModuleDigest().empty() )
structTypeName += "_m" + sd->getModuleDigest();
for ( auto &gp : sd->mGenericParams )
{
auto it2 = typeSub.find( gp.mName );
if ( it2 != typeSub.end() )
structTypeName += "_" + it2->second;
}
}
auto stIt = mStructTypeMap.find( structTypeName );
if ( stIt != mStructTypeMap.end() )
structType = stIt->second;
else
structType = getOrCreateStructType( sd );
const auto &fields = sd->getFields();
bool hasRefField = false;
for ( auto &f : fields )
{
if ( f->getVariableType() == nullptr )
continue;
string fName = f->getVariableType()->getName();
auto subIt = typeSub.find( fName );
if ( subIt != typeSub.end() )
fName = subIt->second;
if ( fName == "string" || fName == "Array" || fName == "Buffer" ||
f->getVariableType()->isFunctionType() ||
isUserStructType( fName ) )
{
hasRefField = true;
break;
}
}
if ( !hasRefField )
{
mStructDtorMap[dtorName] = nullptr;
return nullptr;
}
// Generate the destructor function: void __StructName_dtor(void *ptr)
llvm::Type *ptrType = llvm::PointerType::get( *mContext, 0 );
llvm::FunctionType *dtorFT = llvm::FunctionType::get(
llvm::Type::getVoidTy( *mContext ), { ptrType }, false );
// Check if already declared (e.g., from combine mode)
llvm::Function *dtorFn = mModule->getFunction( dtorName );
if ( dtorFn == nullptr )
{
dtorFn = llvm::Function::Create(
dtorFT, llvm::Function::InternalLinkage, dtorName, mModule.get() );
}
// Save and restore builder state
llvm::BasicBlock *savedBB = mBuilder->GetInsertBlock();
llvm::BasicBlock::iterator savedPt;
bool hadInsertPoint = ( savedBB != nullptr );
if ( hadInsertPoint )
savedPt = mBuilder->GetInsertPoint();
// Create the destructor body
llvm::BasicBlock *entryBB = llvm::BasicBlock::Create(
*mContext, "entry", dtorFn );
mBuilder->SetInsertPoint( entryBB );
llvm::Value *selfPtr = dtorFn->getArg( 0 );
// Release each refcounted field
for ( size_t fi = 0; fi < fields.size(); fi++ )
{
if ( fields[fi]->getVariableType() == nullptr )
continue;
string fieldTypeName = fields[fi]->getVariableType()->getName();
auto subIt = typeSub.find( fieldTypeName );
if ( subIt != typeSub.end() )
fieldTypeName = subIt->second;
if ( fieldTypeName == "string" )
{
llvm::Value *fieldPtr = mBuilder->CreateStructGEP(
structType, selfPtr, fi, "dtor.str.ptr" );
llvm::Value *strVal = mBuilder->CreateLoad( ptrType, fieldPtr, "dtor.str" );
mBuilder->CreateCall( getOrDeclareStringRelease(), { strVal } );
}
else if ( fieldTypeName == "Array" )
{
llvm::Value *fieldPtr = mBuilder->CreateStructGEP(
structType, selfPtr, fi, "dtor.arr.ptr" );
llvm::Value *arrVal = mBuilder->CreateLoad( ptrType, fieldPtr, "dtor.arr" );
mBuilder->CreateCall( getOrDeclareArrayRelease(), { arrVal } );
}
else if ( fieldTypeName == "Buffer" )
{
llvm::Value *fieldPtr = mBuilder->CreateStructGEP(
structType, selfPtr, fi, "dtor.buf.ptr" );
llvm::Value *bufVal = mBuilder->CreateLoad( ptrType, fieldPtr, "dtor.buf" );
mBuilder->CreateCall( getOrDeclareBufferRelease(), { bufVal } );
}
else if ( isUserStructType( fieldTypeName ) )
{
// Nested struct: release via __blang_rc_release (its own destructor runs)
llvm::Value *fieldPtr = mBuilder->CreateStructGEP(
structType, selfPtr, fi, "dtor.struct.ptr" );
llvm::Value *structVal = mBuilder->CreateLoad( ptrType, fieldPtr, "dtor.struct" );
mBuilder->CreateCall( getOrDeclareRcRelease(), { structVal } );
}
else if ( fields[fi]->getVariableType()->isFunctionType() )
{
// Release fn-typed field's lambda context
llvm::Type *pairType = llvm::StructType::get( *mContext, { ptrType, ptrType } );
llvm::Value *fieldPtr = mBuilder->CreateStructGEP(
structType, selfPtr, fi, "dtor.fn.ptr" );
llvm::Value *pairVal = mBuilder->CreateLoad(
pairType, fieldPtr, "dtor.fn.pair" );
llvm::Value *ctxPtr = mBuilder->CreateExtractValue(
pairVal, 1, "dtor.fn.ctx" );
mBuilder->CreateCall( getOrDeclareLambdaCtxRelease(), { ctxPtr } );
}
}
mBuilder->CreateRetVoid();
// Restore builder state
if ( hadInsertPoint )
mBuilder->SetInsertPoint( savedBB, savedPt );
else if ( savedBB != nullptr )
mBuilder->SetInsertPoint( savedBB );
mStructDtorMap[dtorName] = dtorFn;
return dtorFn;
}
llvm::Function *CodeGen::getOrDeclareRcAllocDtor()
{
llvm::Function *fn = mModule->getFunction( "__blang_rc_alloc_dtor" );
if ( fn != nullptr )
return fn;
llvm::Type *ptrType = llvm::PointerType::get( *mContext, 0 );
// void *__blang_rc_alloc_dtor(size_t data_size, void (*dtor)(void*))
llvm::FunctionType *ft = llvm::FunctionType::get(
ptrType,
{ llvm::Type::getInt64Ty( *mContext ), ptrType },
false );
return llvm::Function::Create(
ft, llvm::Function::ExternalLinkage, "__blang_rc_alloc_dtor", mModule.get() );
}
// ---- Cross-module construction ABI: the library-emitted factory ----
std::string CodeGen::mangleStructFactoryName( const std::string &structName,
const std::string &modulePrefix )
{
// Reserved "__" family, alongside __<Struct>_dtor. A user cannot spell this
// (Sema rejects source identifiers that would mangle into the reserved
// family), so it can never collide with a method named `new`.
//
// The module prefix mirrors method mangling (CodeGen.cpp: "net__Socket_read"),
// so two namespaced modules that both define a `Socket` get distinct
// factories rather than silently sharing one symbol.
if ( modulePrefix.empty() )
return "__" + structName + "_new";
return "__" + modulePrefix + "__" + structName + "_new";
}
// The name a CONSUMER derives for an imported struct's factory.
//
// Deliberately prefix-free, and asymmetric with the emitting side above: a
// consumer knows the struct's name but not the defining module's codegen prefix,
// which is not carried in the .bmod. That is sound today because the only
// producers of .bmod files are `bcc build` library projects, which run with no
// module prefix — the namespaced stdlib modules that DO get a prefix are
// combined into the consumer's own compilation, where construction takes the
// inline path and no factory is involved.
//
// If a namespaced module ever ships a .bmod, this asymmetry becomes a link
// error, not a miscompile (the consumer references a symbol the library never
// emitted). Closing it means carrying the defining module's identity in the
// interface — which is Epic B's canonical module identity, not this epic's.
std::string CodeGen::mangleImportedStructFactoryName( const std::string &structName )
{
return mangleStructFactoryName( structName, std::string() );
}
// Build the LLVM signature of a struct's factory: ptr(<init params...>).
// Returns nullptr when the struct has no usable init.
llvm::FunctionType *CodeGen::structFactoryType( StructDefinition *structDef )
{
FunctionDefinition *initMethod = structDef->getInitMethod();
if ( initMethod == nullptr )
return nullptr;
llvm::Type *ptrType = llvm::PointerType::get( *mContext, 0 );
std::vector<llvm::Type*> paramTypes;
for ( auto ¶m : initMethod->mParameters )
{
// Skip the implicit self — the factory allocates it.
if ( param->getVariableType() != nullptr &&
param->getVariableType()->getName() == "self" )
continue;
paramTypes.push_back( getLLVMType( param->getVariableType() ) );
}
return llvm::FunctionType::get( ptrType, paramTypes, false );
}
void CodeGen::genStructFactory( StructDefinition *structDef )
{
if ( structDef == nullptr || structDef->isGeneric() )
return;
// Only a module that owns the type — and therefore its init BODY — can emit
// the factory. A bodyless init means we are looking at an interface record.
FunctionDefinition *initMethod = structDef->getInitMethod();
if ( initMethod == nullptr || initMethod->mFuncBody == nullptr )
return;
std::string factoryName = mangleStructFactoryName( structDef->getName(), mModulePrefix );
// Already present. Two benign ways to get here:
// - combine mode may walk the same module twice;
// - a consumer declared the factory (declareInterfaceStructMembers) and
// then compiled the defining module in the same LLVM module.
// In both cases the existing entry is the same symbol for the same type, so
// re-emitting would be a duplicate definition.
//
// It is keyed on the MANGLED name, so two same-named structs in DIFFERENT
// namespaced modules no longer meet here — they mangle apart now that the
// prefix is included (__a__Thing_new vs __b__Thing_new).
//
// But do NOT read this early return as a duplicate-type guard. Two
// same-named structs under the SAME prefix are not diagnosed anywhere in the
// pipeline: the second definition's addSymbol is silently dropped (design
// record P2) and both share ONE LLVM struct type, so
// - with identical field layouts they silently merge, and
// - with differing layouts the second init indexes past the first's type
// and qcc dies on an LLVM assertion (StructType::getElementType,
// "Element number out of range"), not on a diagnostic.
// That is a pre-existing flat-merge/type-identity defect (P2/P10), owned by
// Epic B's canonical module identity — this early return neither causes it
// nor protects against it.
if ( mModule->getFunction( factoryName ) != nullptr )
return;
// Everything below this point is a compiler invariant, not a user error: we
// have already established the struct is non-generic and has an init WITH A
// BODY. Bailing out silently would emit no factory while a consumer still
// emits `declare ptr @__X_new` — producing exactly the consumer-side link
// error against a generated symbol that this epic exists to eliminate.
llvm::FunctionType *ft = structFactoryType( structDef );
if ( ft == nullptr )
{
std::cerr << "internal compiler error: cannot build a factory signature "
"for struct '" << structDef->getName()
<< "' — please report" << std::endl;
mHasError = true;
return;
}
auto initIt = mFunctionMap.find( initMethod );
if ( initIt == mFunctionMap.end() || initIt->second == nullptr )
{
std::cerr << "internal compiler error: no emitted symbol for the "
"constructor of struct '" << structDef->getName()
<< "' — please report" << std::endl;
mHasError = true;
return;
}
llvm::Function *factory = llvm::Function::Create(
ft, llvm::Function::ExternalLinkage, factoryName, mModule.get() );
// Save builder state — we may be called between other emissions.
llvm::BasicBlock *savedBB = mBuilder->GetInsertBlock();
llvm::BasicBlock::iterator savedPt;
bool hadInsertPoint = ( savedBB != nullptr );
if ( hadInsertPoint )
savedPt = mBuilder->GetInsertPoint();
llvm::BasicBlock *entryBB = llvm::BasicBlock::Create( *mContext, "entry", factory );
mBuilder->SetInsertPoint( entryBB );
llvm::StructType *structType = getOrCreateStructType( structDef );
llvm::DataLayout dl( mModule.get() );
llvm::Value *sizeVal = llvm::ConstantInt::get(
llvm::Type::getInt64Ty( *mContext ), dl.getTypeAllocSize( structType ) );
std::map<std::string, std::string> noSub;
llvm::Function *dtorFn = getOrGenStructDestructor( structDef, noSub );
// The builder may have been repointed while generating the destructor.
mBuilder->SetInsertPoint( entryBB );
llvm::Value *heapPtr = nullptr;
if ( dtorFn != nullptr )
heapPtr = mBuilder->CreateCall( getOrDeclareRcAllocDtor(), { sizeVal, dtorFn }, "new.ptr" );
else
heapPtr = mBuilder->CreateCall( getOrDeclareRcAlloc(), { sizeVal }, "new.ptr" );
std::vector<llvm::Value*> args;
args.push_back( heapPtr );
for ( auto &arg : factory->args() )
args.push_back( &arg );
mBuilder->CreateCall( initIt->second, args );
mBuilder->CreateRet( heapPtr );
if ( hadInsertPoint )
mBuilder->SetInsertPoint( savedBB, savedPt );
else if ( savedBB != nullptr )
mBuilder->SetInsertPoint( savedBB );
}
void CodeGen::declareInterfaceStructMembers( StructDefinition *structDef )
{
// Generic structs ship full layout and bodies in the .bmod; the consumer
// monomorphizes them locally, so there is nothing to declare here.
if ( structDef == nullptr || structDef->isGeneric() )
return;
llvm::Type *ptrType = llvm::PointerType::get( *mContext, 0 );
// The factory, if the interface declared an init.
if ( structDef->getInitMethod() != nullptr )
{
std::string factoryName = mangleImportedStructFactoryName( structDef->getName() );
if ( mModule->getFunction( factoryName ) == nullptr )
{
llvm::FunctionType *ft = structFactoryType( structDef );
if ( ft != nullptr )
llvm::Function::Create( ft, llvm::Function::ExternalLinkage,
factoryName, mModule.get() );
}
}
// Method signatures: declarations only, never definitions.
for ( auto &msp : structDef->mMethods )
{
FunctionDefinition *method = const_cast<FunctionDefinition*>(
(const FunctionDefinition*)msp );
if ( method->isGeneric() || method->isInit() )
continue;
std::string mangledName = structDef->getName() + "_" + method->getName();
if ( mModule->getFunction( mangledName ) != nullptr )
continue;
std::vector<llvm::Type*> paramTypes;
for ( auto ¶m : method->mParameters )
{
if ( param->getVariableType() != nullptr &&
param->getVariableType()->getName() == "self" )
{
paramTypes.push_back( ptrType );
mSelfStructMap[param] = structDef;
}
else
{
paramTypes.push_back( getLLVMType( param->getVariableType() ) );
}
}
llvm::FunctionType *ft = llvm::FunctionType::get(
getLLVMType( method->mReturnType ), paramTypes, method->isVariadic() );
llvm::Function *fn = llvm::Function::Create(
ft, llvm::Function::ExternalLinkage, mangledName, mModule.get() );
mFunctionMap[method] = fn;
}
}
llvm::Value *CodeGen::genStructLiteral( StructLiteralExpression *expr )
{
auto defIt = mStructDefMap.find( expr->mTypeName );
if ( defIt == mStructDefMap.end() )
return nullptr;
StructDefinition *structDef = defIt->second;
llvm::StructType *structType = nullptr;
// Handle generic struct instantiation. The literal's written type args are
// resolved through the ACTIVE substitution first: a `Pair<T> { ... }`
// inside a monomorphized method (T -> string) must instantiate
// Pair_string — instantiating with the raw `T` self-maps the enclosing
// substitution (T -> T) and stamps out a bogus i32-layout Pair_T whose
// 8-byte pointer stores overflow the allocation.
std::map<std::string, Type*> savedSub = mTypeSubstitution;
std::vector<SmartPtr<Type>> resolvedArgs;
for ( auto &ta : expr->mTypeArgs )
{
Type *arg = (Type *)ta;
auto s = savedSub.find( arg->getName() );
if ( s != savedSub.end() && s->second != nullptr &&
s->second->getName() != arg->getName() )
arg = s->second;
resolvedArgs.push_back( arg );
}
if ( !resolvedArgs.empty() && structDef->isGeneric() )
{
structType = instantiateGenericStruct( structDef, resolvedArgs );
// Re-establish substitution map for field value generation
for ( size_t i = 0; i < structDef->mGenericParams.size() && i < resolvedArgs.size(); i++ )
{
SmartPtr<Type> arg = resolvedArgs[i];
mTypeSubstitution[structDef->mGenericParams[i].mName] = (Type*)arg;
}
}
else
{
structType = getOrCreateStructType( structDef );
}
// Heap-allocate the struct via ARC with a destructor for refcounted field cleanup
llvm::DataLayout dl( mModule.get() );
uint64_t dataSize = dl.getTypeAllocSize( structType );
llvm::Value *sizeVal = llvm::ConstantInt::get(
llvm::Type::getInt64Ty( *mContext ), dataSize );
// Build type substitution map for destructor generation
std::map<std::string, std::string> dtorSub;
if ( !expr->mTypeArgs.empty() && structDef->isGeneric() )
{
for ( size_t i = 0; i < structDef->mGenericParams.size() && i < expr->mTypeArgs.size(); i++ )
{
SmartPtr<Type> arg = expr->mTypeArgs[i];
string resolvedName = arg->getName();
auto sIt2 = mTypeSubstitution.find( resolvedName );
if ( sIt2 != mTypeSubstitution.end() )
resolvedName = sIt2->second->getName();
dtorSub[structDef->mGenericParams[i].mName] = resolvedName;
}
}
llvm::Function *dtorFn = getOrGenStructDestructor( structDef, dtorSub );
llvm::Value *heapPtr = nullptr;
if ( dtorFn != nullptr )
{
heapPtr = mBuilder->CreateCall(
getOrDeclareRcAllocDtor(), { sizeVal, dtorFn }, "struct.ptr" );
}
else
{
heapPtr = mBuilder->CreateCall(
getOrDeclareRcAlloc(), { sizeVal }, "struct.ptr" );
}
// Store each field value
for ( size_t i = 0; i < expr->mFieldNames.size(); i++ )
{
// Find the field index in the struct definition
int fieldIdx = -1;
for ( size_t f = 0; f < structDef->mFields.size(); f++ )
{
if ( structDef->mFields[f]->getName() == expr->mFieldNames[i] )
{
fieldIdx = static_cast<int>( f );
break;
}
}
if ( fieldIdx < 0 )
continue;
llvm::Value *fieldVal = nullptr;
// For empty array literals assigned to Array<T> fields, use the correct
// element size from the field's type parameter instead of the default 4
auto *arrLit = dynamic_cast<ArrayLiteralExpression*>( (Expression*)expr->mFieldValues[i] );
if ( arrLit != nullptr && arrLit->mElements.empty() )
{
Type *fieldType = structDef->mFields[fieldIdx]->getVariableType();
if ( fieldType != nullptr && fieldType->getName() == "Array" &&
fieldType->getNumTypeParams() > 0 )
{
Type *elemType = fieldType->getTypeParam( 0 );
string elemTypeName = elemType->getName();
auto subIt = mTypeSubstitution.find( elemTypeName );
if ( subIt != mTypeSubstitution.end() )
elemType = subIt->second;
llvm::Type *llvmElemType = getLLVMType( elemType );
llvm::DataLayout dl( mModule.get() );
int elemSize = dl.getTypeAllocSize( llvmElemType );
llvm::Value *elemSizeVal = llvm::ConstantInt::get(
llvm::Type::getInt32Ty( *mContext ), elemSize );
llvm::Value *capVal = llvm::ConstantInt::get(
llvm::Type::getInt64Ty( *mContext ), 8 );
fieldVal = mBuilder->CreateCall(
getOrDeclareArrayCreate(), { elemSizeVal, capVal }, "arr" );
// Set element destructor for refcounted element types
string resolvedElemName = elemType->getName();
auto subIt2 = mTypeSubstitution.find( resolvedElemName );
if ( subIt2 != mTypeSubstitution.end() )
resolvedElemName = subIt2->second->getName();
emitArrayElemDtor( fieldVal, resolvedElemName );
}
}
if ( fieldVal == nullptr )
{
// NON-empty array literal into an Array<T> field: pass the field's
// element type as the literal hint (resolving generic params via the
// active substitution), exactly like the var-decl site. Without it
// genArrayLiteral falls back to i32 elements — no push-retain and no
// element destructor — so refcounted elements of a literal nested in
// a struct literal (Box<string>{ items: ["a","b"] }) were freed by
// the statement-end temp release while the array still held them.
if ( arrLit != nullptr && !arrLit->mElements.empty() &&
fieldIdx >= 0 && (size_t)fieldIdx < structDef->mFields.size() )
{
Type *declFieldType = structDef->mFields[fieldIdx]->getVariableType();
if ( declFieldType != nullptr && declFieldType->getName() == "Array" &&
declFieldType->getNumTypeParams() > 0 )
{
Type *elemType = declFieldType->getTypeParam( 0 );
auto subIt = mTypeSubstitution.find( elemType->getName() );
if ( subIt != mTypeSubstitution.end() )
elemType = subIt->second;
mArrayElemTypeHint = getLLVMType( elemType );
mArrayElemTypeNameHint = elemType->getName();
}
}
fieldVal = genExpression( expr->mFieldValues[i] );
}
if ( fieldVal == nullptr )
continue;
llvm::Value *fieldPtr = mBuilder->CreateStructGEP( structType, heapPtr, fieldIdx, "field" );
// Coerce value type to match field type (e.g., double literal -> float field)
llvm::Type *fieldType = structType->getElementType( fieldIdx );
if ( fieldVal->getType() != fieldType )
{
if ( fieldType->isFloatTy() && fieldVal->getType()->isDoubleTy() )
fieldVal = mBuilder->CreateFPTrunc( fieldVal, fieldType, "fptrunc" );
else if ( fieldType->isDoubleTy() && fieldVal->getType()->isFloatTy() )
fieldVal = mBuilder->CreateFPExt( fieldVal, fieldType, "fpext" );
else if ( fieldType->isIntegerTy() && fieldVal->getType()->isIntegerTy() )
{
bool isSigned = true;
if ( fieldIdx >= 0 && (size_t)fieldIdx < structDef->mFields.size() )
{
Type *ft = structDef->mFields[fieldIdx]->getVariableType();
if ( ft != nullptr && ft->getName() == "byte" )
isSigned = false;
}
fieldVal = mBuilder->CreateIntCast( fieldVal, fieldType,
isSigned, "icast" );
}
}
mBuilder->CreateStore( fieldVal, fieldPtr );
// Retain refcounted fields stored into the struct.
// For strings: always retain (temp string release balances the extra refcount).
// For arrays/buffers/structs from variables/field accesses: retain (source keeps ref).
// For arrays/buffers/structs from literals/calls: skip retain (ownership transfers).
if ( fieldIdx >= 0 && (size_t)fieldIdx < structDef->mFields.size() )
{
Type *fType = structDef->mFields[fieldIdx]->getVariableType();
if ( fType != nullptr )
{
string fTypeName = fType->getName();
// Resolve generic type params (e.g., T -> string)
auto subIt = mTypeSubstitution.find( fTypeName );
if ( subIt != mTypeSubstitution.end() )
fTypeName = subIt->second->getName();
// Check if source is an existing owner (variable/field access)
bool srcIsExistingOwner = false;
{
auto *se = (Expression*)expr->mFieldValues[i];
srcIsExistingOwner = ( dynamic_cast<VariableExpression*>( se ) != nullptr ||
dynamic_cast<FieldAccessExpression*>( se ) != nullptr );
}
if ( fTypeName == "string" )
mBuilder->CreateCall( getOrDeclareStringRetain(), { fieldVal } );
else if ( fTypeName == "Array" && srcIsExistingOwner )
mBuilder->CreateCall( getOrDeclareArrayRetain(), { fieldVal } );
else if ( fTypeName == "Array" && !srcIsExistingOwner )
// Fresh array rvalue (call/method result) transfers ownership
// into the struct field — untrack so it is not also released as
// a statement temporary (which would double-free).
untrackTempArray( fieldVal );
else if ( fTypeName == "Buffer" && srcIsExistingOwner )
mBuilder->CreateCall( getOrDeclareBufferRetain(), { fieldVal } );
else if ( isUserStructType( fTypeName ) && srcIsExistingOwner )
mBuilder->CreateCall( getOrDeclareRcRetain(), { fieldVal } );
else if ( isUserStructType( fTypeName ) && !srcIsExistingOwner )
untrackTempStruct( fieldVal );
else if ( fType->isFunctionType() )
{
llvm::Value *ctxPtr = mBuilder->CreateExtractValue(
fieldVal, 1, "sl.fn.ctx" );
mBuilder->CreateCall( getOrDeclareLambdaCtxRetain(), { ctxPtr } );
}
}
}
}
// Restore substitution map
mTypeSubstitution = savedSub;
// Track this struct as a temporary — it will be released after the enclosing
// statement unless it gets stored into a variable (which untracts it).
trackTempStruct( heapPtr );
// Return the heap pointer (struct is by-reference)
return heapPtr;
}
llvm::Value *CodeGen::genConstructExpression( ConstructExpression *expr )
{
StructDefinition *structDef = expr->mStructDef;
if ( structDef == nullptr )
return nullptr;
string structName = structDef->getName();
llvm::StructType *structType = nullptr;
// Cross-module construction: a struct that arrived through a .bmod has no
// field layout here, so the caller-allocating path below cannot size it or
// build its destructor. Call the factory the defining module emitted.
// Generic structs are excluded by design — their bodies ship in the .bmod
// and the consumer monomorphizes them, so it does have their layout.
if ( structDef->isFromInterface() && !structDef->isGeneric() )
{
if ( structDef->getInitMethod() == nullptr )
{
reportError( expr, "type '" + structName +
"' has no constructor visible from this module" );
return nullptr;
}
std::string factoryName = mangleImportedStructFactoryName( structName );
llvm::Function *factory = mModule->getFunction( factoryName );
if ( factory == nullptr )
{
llvm::FunctionType *ft = structFactoryType( structDef );
if ( ft == nullptr )
{
reportError( expr, "cannot construct '" + structName +
"': its interface declares no usable constructor" );
return nullptr;
}
factory = llvm::Function::Create( ft, llvm::Function::ExternalLinkage,
factoryName, mModule.get() );
}
std::vector<llvm::Value*> args;
for ( auto &argExpr : expr->mArgs )
{
llvm::Value *argVal = genExpression( argExpr );
if ( argVal == nullptr )
return nullptr;
args.push_back( argVal );
}
if ( args.size() != factory->getFunctionType()->getNumParams() )
{
reportError( expr, "wrong number of arguments constructing '" +
structName + "'" );
return nullptr;
}
llvm::Value *heapPtr = mBuilder->CreateCall( factory, args, "ctor.ptr" );
trackTempStruct( heapPtr );
return heapPtr;
}
// Handle generic struct instantiation (written type args resolved through
// the active substitution — see genStructLiteral for the rationale)
std::map<std::string, Type*> savedSub = mTypeSubstitution;
std::vector<SmartPtr<Type>> resolvedArgs;
for ( auto &ta : expr->mTypeArgs )
{
Type *arg = (Type *)ta;
auto s = savedSub.find( arg->getName() );
if ( s != savedSub.end() && s->second != nullptr &&
s->second->getName() != arg->getName() )
arg = s->second;
resolvedArgs.push_back( arg );
}
if ( !resolvedArgs.empty() && structDef->isGeneric() )
{
structType = instantiateGenericStruct( structDef, resolvedArgs );
for ( size_t i = 0; i < structDef->mGenericParams.size() && i < resolvedArgs.size(); i++ )
{
SmartPtr<Type> arg = resolvedArgs[i];
mTypeSubstitution[structDef->mGenericParams[i].mName] = (Type*)arg;
}
structName = mangleGenericName( structName, resolvedArgs );
}
else
{
structType = getOrCreateStructType( structDef );
}
// Heap-allocate the struct with ARC
llvm::DataLayout dl( mModule.get() );
uint64_t dataSize = dl.getTypeAllocSize( structType );
llvm::Value *sizeVal = llvm::ConstantInt::get(
llvm::Type::getInt64Ty( *mContext ), dataSize );
std::map<std::string, std::string> dtorSub;
if ( !expr->mTypeArgs.empty() && structDef->isGeneric() )
{
for ( size_t i = 0; i < structDef->mGenericParams.size() && i < expr->mTypeArgs.size(); i++ )
{
SmartPtr<Type> arg = expr->mTypeArgs[i];
string resolvedName = arg->getName();
auto sIt2 = mTypeSubstitution.find( resolvedName );
if ( sIt2 != mTypeSubstitution.end() )
resolvedName = sIt2->second->getName();
dtorSub[structDef->mGenericParams[i].mName] = resolvedName;
}
}
llvm::Function *dtorFn = getOrGenStructDestructor( structDef, dtorSub );
llvm::Value *heapPtr = nullptr;
if ( dtorFn != nullptr )
heapPtr = mBuilder->CreateCall( getOrDeclareRcAllocDtor(), { sizeVal, dtorFn }, "ctor.ptr" );
else
heapPtr = mBuilder->CreateCall( getOrDeclareRcAlloc(), { sizeVal }, "ctor.ptr" );
// Call the init method: StructName_init(heapPtr, args...)
string initName = structName + "_init";
llvm::Function *initFn = mModule->getFunction( initName );
// Also try with module prefix
if ( initFn == nullptr )
{
for ( auto &fn : *mModule )
{
string fname = fn.getName().str();
if ( fname.size() > initName.size() + 2 &&
fname.substr( fname.size() - initName.size() ) == initName &&
fname[fname.size() - initName.size() - 1] == '_' &&
fname[fname.size() - initName.size() - 2] == '_' )
{
initFn = &fn;
break;
}
}
}
if ( initFn == nullptr )
{
// Check mFunctionMap for the init method
FunctionDefinition *initMethod = structDef->getInitMethod();
if ( initMethod != nullptr )
{
auto fIt = mFunctionMap.find( initMethod );
if ( fIt != mFunctionMap.end() )
initFn = fIt->second;
}
}
if ( initFn == nullptr )
{
// Falling through here would hand back a heap block that no init ever
// wrote — and for a struct with no resolvable layout getTypeAllocSize
// yields 1 byte, so every later field access reads out of bounds.
// Refuse loudly instead (Principle III).
mTypeSubstitution = savedSub;
reportError( expr, "cannot construct '" + structName +
"': no constructor was found for this type" );
return nullptr;
}
std::vector<llvm::Value*> args;
args.push_back( heapPtr ); // self
for ( auto &argExpr : expr->mArgs )