diff --git a/src/ast/ast_generate.cpp b/src/ast/ast_generate.cpp index 05e350cb37..fa7c227ae6 100644 --- a/src/ast/ast_generate.cpp +++ b/src/ast/ast_generate.cpp @@ -2296,8 +2296,8 @@ namespace das { auto rttiType = new TypeDecl(baseClass); rttiType->at = baseClass->at; ExpressionPtr finit = new ExprTypeInfo(baseClass->at, "rtti_classinfo", rttiType); - if ( baseClass->parent ) { - auto fd = (Structure::FieldDeclaration *) baseClass->findField("__rtti"); + auto fd = baseClass->parent ? (Structure::FieldDeclaration *) baseClass->findField("__rtti") : nullptr; + if ( fd ) { fd->init = finit; fd->parentType = fd->type->isAuto(); fd->generated = true; @@ -2323,8 +2323,8 @@ namespace das { // template classes stay on open "_::": their stamped instances infer in the consumer // module while the stamped finalizer may live elsewhere, so a strict pin can't see it ExpressionPtr finit = new ExprAddr(baseClass->at, (baseClass->isTemplate ? "_::" : "__::") + fname); - if ( baseClass->parent ) { - auto fd = (Structure::FieldDeclaration *) baseClass->findField("__finalize"); + auto fd = baseClass->parent ? (Structure::FieldDeclaration *) baseClass->findField("__finalize") : nullptr; + if ( fd ) { auto castT = new TypeDecl(Type::autoinfer, baseClass->at); fd->init = new ExprCast(baseClass->at, finit, castT); fd->parentType = fd->type->isAuto(); diff --git a/src/ast/ast_infer_type.cpp b/src/ast/ast_infer_type.cpp index 1ca09e8dbb..d78f4eaacc 100644 --- a/src/ast/ast_infer_type.cpp +++ b/src/ast/ast_infer_type.cpp @@ -2716,6 +2716,7 @@ namespace das { return new ExprConstBool(expr->at, ft != nullptr); } else { if (expr->trait == "safe_has_field") { + reportAstChanged(); return new ExprConstBool(expr->at, false); } else { error("typeinfo(has_field<" + expr->subtrait + "> ...) is only defined for structures and handled types, " + describeType(expr->typeexpr), "", "", @@ -2730,6 +2731,7 @@ namespace das { return new ExprConstBool(expr->at, it != ann.end()); } else { if (expr->trait == "struct_safe_has_annotation") { + reportAstChanged(); return new ExprConstBool(expr->at, false); } else { error("typeinfo(struct_has_annotation<" + expr->subtrait + "> ...) is only defined for structures, " + describeType(expr->typeexpr), "", "", @@ -2742,6 +2744,7 @@ namespace das { auto it = find_if(ann.begin(), ann.end(), [&](const AnnotationDeclarationPtr &pa) { return pa->annotation->name == expr->subtrait; }); if (it == ann.end()) { if (expr->trait == "struct_safe_has_annotation_argument") { + reportAstChanged(); return new ExprConstBool(expr->at, false); } else { error("typeinfo(struct_has_annotation_argument<" + expr->subtrait + ";" + expr->extratrait + "> ...) annotation not found ", "", "", @@ -2755,6 +2758,7 @@ namespace das { } } else { if (expr->trait == "struct_safe_has_annotation_argument") { + reportAstChanged(); return new ExprConstBool(expr->at, false); } else { error("typeinfo(struct_has_annotation_argument<" + expr->subtrait + "> ...) is only defined for structures, " + describeType(expr->typeexpr), "", "", @@ -4600,6 +4604,9 @@ namespace das { return Visitor::visit(expr); } expr->variable = var; + if (var->type->isExprType()) { + return Visitor::visit(expr); + } TypeDecl::clone(expr->type, var->type); expr->type->ref = true; return Visitor::visit(expr); diff --git a/src/ast/ast_unused.cpp b/src/ast/ast_unused.cpp index 0915625701..e3da3ecfa0 100644 --- a/src/ast/ast_unused.cpp +++ b/src/ast/ast_unused.cpp @@ -690,6 +690,7 @@ namespace das { // Op1 virtual void preVisit ( ExprOp1 * expr ) override { Visitor::preVisit(expr); + if ( !expr->func ) return; auto sef = getSideEffects(expr->func); markPassMutableOperand(expr->func, 0, expr->subexpr); if ( sef & uint32_t(SideEffects::modifyArgument) ) { @@ -699,6 +700,7 @@ namespace das { // Op2 virtual void preVisit ( ExprOp2 * expr ) override { Visitor::preVisit(expr); + if ( !expr->func ) return; auto sef = getSideEffects(expr->func); markPassMutableOperand(expr->func, 0, expr->left); markPassMutableOperand(expr->func, 1, expr->right); @@ -716,12 +718,11 @@ namespace das { // Op3 virtual void preVisit ( ExprOp3 * expr ) override { Visitor::preVisit(expr); - auto sef = expr->func ? getSideEffects(expr->func) : 0; - if ( expr->func ) { - markPassMutableOperand(expr->func, 0, expr->subexpr); - markPassMutableOperand(expr->func, 1, expr->left); - markPassMutableOperand(expr->func, 2, expr->right); - } + if ( !expr->func ) return; + auto sef = getSideEffects(expr->func); + markPassMutableOperand(expr->func, 0, expr->subexpr); + markPassMutableOperand(expr->func, 1, expr->left); + markPassMutableOperand(expr->func, 2, expr->right); if ( sef & uint32_t(SideEffects::modifyArgument) ) { auto condT = expr->subexpr->type; if ( condT->isRefOrPointer() && !condT->constant ) { diff --git a/tests/language/failed_class_parent_rejected.das b/tests/language/failed_class_parent_rejected.das new file mode 100644 index 0000000000..16328231aa --- /dev/null +++ b/tests/language/failed_class_parent_rejected.das @@ -0,0 +1,13 @@ +// A class deriving from a class whose own declaration is rejected. The `class B : A` error +// returns before B gets its generated __rtti and __finalize fields, so `class C : B` - which is +// accepted - reaches field generation with a parent that carries neither. +options gen2 +expect 30134 + +struct A; +class B : A; +class C : B; + +[export] +def main { +} diff --git a/tests/language/failed_global_dim_self_reference.das b/tests/language/failed_global_dim_self_reference.das new file mode 100644 index 0000000000..00350f5d3d --- /dev/null +++ b/tests/language/failed_global_dim_self_reference.das @@ -0,0 +1,14 @@ +// A global whose fixed-array dimension names the variable it declares. The dimension expression +// sits inside the type it dimensions, so a reference that takes the variable's own type takes a +// type holding that reference; with two dimensions each infer pass squares the last one's copy. +// The array clone in main is what keeps the passes coming. +options gen2 +expect 30109:2 + +let zz : int[zz][zz] + +[export] +def main { + var cb : array + cb := cb +} diff --git a/tests/language/failed_op2_typeinfo_safe_traits.das b/tests/language/failed_op2_typeinfo_safe_traits.das new file mode 100644 index 0000000000..5df736b235 --- /dev/null +++ b/tests/language/failed_op2_typeinfo_safe_traits.das @@ -0,0 +1,28 @@ +// A safe_* typeinfo trait that answers false replaces its node, so it tells infer to run again. +// Each arm takes its value from a nested typeinfo, or from an index that folds, so the trait +// resolves one pass late - in a pass where nothing else changes, which is the pass that catches a +// missing signal. Keep this file free of anything that keeps signalling (a print's string builder +// will do it): that grants the enclosing operator a repair pass and the arms stop proving anything. +// Without the signal every arm reports 50613 instead, the reported input included. +options gen2 +expect 30341 + +[comment(i = 1)] +struct TsrAnnotated { + x : int +} + +var g_tsr_annotated : TsrAnnotated[8] + +[export] +def main { + var a = true + a &&= typeinfo safe_has_field < no_such_field > (typeinfo is_ref(1)) + var b = true + b &&= typeinfo struct_safe_has_annotation < no_such_annotation > (typeinfo is_ref(1)) + var c = true + c &&= typeinfo struct_safe_has_annotation_argument < no_such_annotation; no_such_argument > (typeinfo is_ref(1)) + var d = true + d &&= typeinfo struct_safe_has_annotation_argument < no_such_annotation; no_such_argument > (g_tsr_annotated[typeinfo sizeof(type)]) + 1 &&= typeinfo safe_has_field < no_such_type > (typeinfo is_ref(1)) +}